Created
April 4, 2024 04:46
-
-
Save huynhducduy/4bdc62add6717cee52eaced750d1e5cf to your computer and use it in GitHub Desktop.
useStateAndRef
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {useReducer, useRef} from 'react' | |
import {useCallback} from '@/utils/hooks/useCallback' | |
/* | |
* This hook create a ref that mirror every change to the state | |
* This is useful when we have an effect or memo that read value from state, but dont want to react to state change => use the ref for reading | |
* Future react version will add a support for `useEffectEvent` https://react.dev/reference/react/experimental_useEffectEvent which can solve the case | |
* This hook have 2 modes: | |
* 1. sync: (default mode) ref value and react state value will be update by react scheduler at the sametime that determined by the scheduler (usually lag behind the method call) | |
* 2. refFirst: Fef value will update right when the setter is called, react state will lag behind with the control of react scheduler. | |
* Note that there will be cases where the react state at the time of update by react scheduler, can be different from the ref that updated immedially before. | |
* Use as your own risk | |
*/ | |
export default function useStateAndRef<T>( | |
defaultValue: T, | |
mode: 'refFirst' | 'sync' = 'sync', | |
customAction?: (value: T, prev: T) => void, | |
) { | |
const ref = useRef<T>(defaultValue) | |
const [state, setter] = useReducer((curr: T, value: T | ((curr: T) => T)) => { | |
// This function will stay in the hooks queue and invoke by react scheduler | |
const next = value instanceof Function ? value(curr) : value | |
// And ref will update with it | |
ref.current = next | |
customAction?.(next, curr) | |
return next | |
}, defaultValue) | |
// This function will be invoke immediately, use as your own risk | |
const immediatelySetter = useCallback((value: T | ((curr: T) => T)) => { | |
const next = value instanceof Function ? value(ref.current) : value | |
// Update ref immediately | |
ref.current = next | |
// Then leave the state for the react scheduler to update | |
setter(next) | |
}, []) | |
return [state, ref, mode === 'sync' ? setter : immediatelySetter] as const | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment