Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save myobie/60fcf162a5ec9dcae74bf8e55bd65b4f to your computer and use it in GitHub Desktop.
Save myobie/60fcf162a5ec9dcae74bf8e55bd65b4f to your computer and use it in GitHub Desktop.
Reactive `useState` variant
import React from 'react'
/**
* A variant of `React.useState` which allows the `inputState` to change over time as well.
* Important: This hook is synchronous / single-render-pass (i.e. doesn't use `useEffect` or `setState` directly).
*/
const useStateWithReactiveInput = <T>(inputState: T): [T, (newState: T | ((prev: T) => T)) => void] => {
const [_, rerender] = React.useState(0)
const stateRef = React.useMemo<{ current: T }>(() => ({ current: inputState }), [])
if (stateRef.current !== inputState) {
// NOTE we don't need to re-render here, because the component is already re-rendering due to the `inputState` change
stateRef.current = inputState
}
const setStateAndRerender = React.useCallback(
(newState: ((prev: T) => T) | T) => {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/37663
const val = typeof newState === 'function' ? newState(stateRef.current) : newState
stateRef.current = val
rerender((c) => c + 1)
},
[rerender],
)
return [stateRef.current, setStateAndRerender]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment