I have some variables of which I need the reference as well as the state. I found something here, that helped me: https://stackoverflow.com/a/58349377/7977410
Pretty much it just kept two variables synchronized (in pseudo code):
const [track, setTrack] = useState('INIT');
const trackRef = useRef('INIT');
// Whenever changing, just both are updated
setTrack('newValue');
trackRef.current = 'newValue';
I was wondering if it was beneficial to combine those two into a new hook. Something like this:
const useStateRef(initialState: any) {
const [state, setState] = useState<typeof initialState>(initialState);
const ref = useRef<typeof initialState>(initialState);
useEffect(() => {
setState(ref);
}, [ref]);
return [state, ref];
}
What would be the best way to do this? Is it even critical to do something like this?
(The background is, I have some self-repeating functions, that need the reference to change what they are doing. But I also need the state variable to rerender visible changes when the same variables are changing... Maybe there is a completely different way to do this, but I am still curious about this approach.)
It's possible, but to make the typings proper, you should use generics instead of any
, and the effect hook needs to be reversed - change the ref.current
when the state
changes. You'll also want to return the state setter in order to change the value in the consumer of useStateRef
.
const useStateRef = <T extends unknown>(initialState: T) => {
const [state, setState] = useState(initialState);
const ref = useRef(initialState);
useEffect(() => {
ref.current = state;
}, [state]);
// Use "as const" below so the returned array is a proper tuple
return [state, setState, ref] as const;
};
Or, to update synchronously, remove the effect hook:
if (ref.current !== state) ref.current = state;
Also note that there should never be any need to have a ref that only ever contains a primitive. const trackRef = useRef('INIT');
can be refactored away entirely and replaced with track
. Refs are generally useful when dealing with objects, like HTMLElements.