Skip to content

Commit 047ac4b

Browse files
authored
Merge pull request #22 from react-restart/new-effects
New effects
2 parents ccd2bef + 11a05a9 commit 047ac4b

13 files changed

+182
-1
lines changed

src/useCallbackRef.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import { useState } from 'react'
2121
*
2222
* return <div ref={attachRef} />
2323
* ```
24+
*
25+
* @category refs
2426
*/
2527
export default function useCallbackRef<TValue = unknown>(): [
2628
TValue | null,

src/useImmediateUpdateEffect.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import useStableMemo from './useStableMemo'
1414
* setValue(value)
1515
* }, [value])
1616
* ```
17+
*
18+
* @category effects
1719
*/
1820
function useImmediateUpdateEffect(effect: () => void, deps: DependencyList) {
1921
const firstRef = useRef(true)

src/useIsomorphicEffect.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ const isDOM = typeof document !== 'undefined'
1414
* Only useful to avoid the console warning.
1515
*
1616
* PREFER `useEffect` UNLESS YOU KNOW WHAT YOU ARE DOING.
17+
*
18+
* @category effects
1719
*/
1820
export default isDOM || isReactNative ? useLayoutEffect : useEffect

src/useMergedRefs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export function mergeRefs<T>(refA?: Ref<T> | null, refB?: Ref<T> | null) {
3333
*
3434
* @param refA A Callback or mutable Ref
3535
* @param refB A Callback or mutable Ref
36+
* @category refs
3637
*/
3738
function useMergedRefs<T>(refA?: Ref<T> | null, refB?: Ref<T> | null) {
3839
return useMemo(() => mergeRefs(refA, refB), [refA, refB])

src/useMountEffect.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useEffect, EffectCallback } from 'react'
2+
3+
/**
4+
* Run's an effect on mount, and is cleaned up on unmount. Generally
5+
* useful for interop with non-react plugins or components
6+
*
7+
* ```ts
8+
* useMountEffect(() => {
9+
* const plugin = $.myPlugin(ref.current)
10+
*
11+
* return () => {
12+
* plugin.destroy()
13+
* }
14+
* })
15+
* ```
16+
* @param effect An effect to run on mount
17+
*
18+
* @category effects
19+
*/
20+
function useMountEffect(effect: EffectCallback) {
21+
return useEffect(effect, [])
22+
}
23+
24+
export default useMountEffect
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useRef } from 'react'
2+
3+
const dft: any = Symbol('default value sigil')
4+
5+
/**
6+
* Exactly the same as `useRef` except that the initial value is set via a
7+
* factroy function. Useful when the default is relatively costly to construct.
8+
*
9+
* ```ts
10+
* const ref = useRefWithInitialValueFactory<ExpensiveValue>(() => constructExpensiveValue())
11+
*
12+
* ```
13+
*
14+
* @param initialValueFactory A factory function returning the ref's default value
15+
* @category refs
16+
*/
17+
export default function useRefWithInitialValueFactory<T>(
18+
initialValueFactory: () => T,
19+
) {
20+
const ref = useRef<T>(dft)
21+
if (ref.current === dft) {
22+
ref.current = initialValueFactory()
23+
}
24+
return ref
25+
}

src/useUpdateEffect.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useEffect, EffectCallback, DependencyList, useRef } from 'react'
2+
3+
/**
4+
* Runs an effect only when the dependencies have changed, skipping the
5+
* initial "on mount" run. Caution, if the dependency list never changes,
6+
* the effect is **never run**
7+
*
8+
* ```ts
9+
* const ref = useRef<HTMLInput>(null);
10+
*
11+
* // focuses an element only if the focus changes, and not on mount
12+
* useUpdateEffect(() => {
13+
* const element = ref.current?.children[focusedIdx] as HTMLElement
14+
*
15+
* element?.focus()
16+
*
17+
* }, [focusedIndex])
18+
* ```
19+
* @param effect An effect to run on mount
20+
*
21+
* @category effects
22+
*/
23+
function useUpdateEffect(fn: EffectCallback, deps: DependencyList) {
24+
const isFirst = useRef(true)
25+
useEffect(() => {
26+
if (isFirst.current) {
27+
isFirst.current = false
28+
return
29+
}
30+
return fn()
31+
}, deps)
32+
}
33+
34+
export default useUpdateEffect

src/useUpdatedRef.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useRef } from 'react'
44
* Returns a ref that is immediately updated with the new value
55
*
66
* @param value The Ref value
7+
* @category refs
78
*/
89
export default function useUpdatedRef<T>(value: T) {
910
const valueRef = useRef<T>(value)

src/useWillUnmount.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useEffect } from 'react'
55
* Attach a callback that fires when a component unmounts
66
*
77
* @param fn Handler to run when the component unmounts
8+
* @category effects
89
*/
910
export default function useWillUnmount(fn: () => void) {
1011
const onUnmount = useUpdatedRef(fn)

test/useImmediateUpdateEffect.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import useImmediateUpdateEffect from '../src/useImmediateUpdateEffect'
22
import { renderHook } from './helpers'
33

44
describe('useImmediateUpdateEffect', () => {
5-
it('should return a function that returns mount state', () => {
5+
it('should run update after value changes', () => {
66
const spy = jest.fn()
77

88
const [, wrapper] = renderHook(

0 commit comments

Comments
 (0)