Saturday 17 July 2021

Store a callback in useRef()

Here is an example of a mutable ref storing the current callback from the Overreacted blog:

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // update ref before 2nd effect
  useEffect(() => {
    savedCallback.current = callback; // save the callback in a mutable ref
  });

  useEffect(() => {
    function tick() {
      // can always access the most recent callback value without callback dep 
      savedCallback.current(); 
    }

    let id = setInterval(tick, delay);
    return () => clearInterval(id);
  }, [delay]);
}

However the React Hook FAQ states that the pattern is not recommended:

Also note that this pattern might cause problems in the concurrent mode. [...]

In either case, we don’t recommend this pattern and only show it here for completeness.

I found this pattern to be very useful in particular for callbacks and don't understand why it gets a red flag in the FAQ. For example, a client component can use useInterval without needing to wrap useCallback around the callback (simpler API).

Also there shouldn't be a problem in concurrent mode, as we update the ref inside useEffect. From my point of view, the FAQ entry might have a wrong point here (or I have misunderstood it).

So, in summary:

  1. Does anything fundamentally speak against storing callbacks inside mutable refs?
  2. Is it safe in concurrent mode when done like it is in the above code, and if not, why not?


from Store a callback in useRef()

No comments:

Post a Comment