React state hook with live updates and closure avoiding reference

Spread the love

Sometime we need to access the value of state before react completes in work of state update, it’s kind of live value that would eventually be stored in the state variable of react. e.g. there may be function which needs latest value of state in the same event loop and may cause issues if it updates in the next event loop. I had this issue when I was using gestures and drawing shapes. I had extensive use of react memoization techniques. Now because of this some of my components were updating on next state update (I know i may have done something wrong, as the project contained code from some novice devs too, and didn’t had time to refactor 🙁 ). So I needed the values whenever they were updating in the same event loop.

The other use case could be we need to access the state value in the callbacks (closure issues). If we access a state value in closure then it will never be updated, instead old value of it will be referenced (closure)

So the below hook will solve all your problems regarding the access to latest state value:

import React, {MutableRefObject, useEffect, useRef, useState} from 'react';

export const useStateWithRef = <T>(initState: T) => {
  const [state, updateState] = useState(initState);
  const stateRef = useRef<T>(initState); // Ref after react has updated the state value
  const stateLiveRef = useRef<T>(initState); // The most recent value. state may be stale, but it will never be stale.

// save the state value to a ref, for closure latest access.
  useEffect(() => {
    stateRef.current = state;
  }, [state]);

  const setState = (value: React.SetStateAction<T>) => {
    const newValue =
      typeof value === 'function'
        ? (value as any)(stateLiveRef.current)
        : value;
    stateLiveRef.current = newValue; // save the most recent value.
    updateState(stateLiveRef.current);
  };

  return [state, setState, stateRef, stateLiveRef] as [
    T,
    React.Dispatch<React.SetStateAction<T>>,
    MutableRefObject<T>, // value will same as state.
    MutableRefObject<T>, // value could be different but state will always have this value eventually
  ];
};

Cheers and Peace out!!!