Skip to content

Instantly share code, notes, and snippets.

@huynhducduy
Created April 4, 2024 04:46
Show Gist options
  • Save huynhducduy/4bdc62add6717cee52eaced750d1e5cf to your computer and use it in GitHub Desktop.
Save huynhducduy/4bdc62add6717cee52eaced750d1e5cf to your computer and use it in GitHub Desktop.
useStateAndRef
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