React - useCallback vs useState's setState(prevState... difference for accessing previous state

Viewed 247

I know that useCallback recreates a function when its dependencies change, so it is some kind of wrapper for memoizing functions, useful for accessing the most updated state in useEffects callbacks (for example).

My question here is simple. Is there any difference for accessing the most freshed state value between using useCallback(() => {}, [carData]) and setCarData(prevCarData => console.log(`Most freshed state: ${JSON.stringify(prevCarData)}`);

I mean, can I get into troubles with the second way? Or the only difference is the memoization of the function?

UPDATE

A)

const memoizedFunc = useCallback(() => {
     ...
      setCarData({...carData, maxVelocity: 50});
}, [carData]);

useEffect(() => {
   if (!carData.maxVelocity) {
      memoizedFunc();
   }
}, [..., memoizedFunc]);

B)

const func = () => {
    setCarData((prevCarData) => ({...prevCarData, maxVelocity: 50}));
};

useEffect(() => {
   if (!carData.maxVelocity) {
       func();
   }
}, [...]);

Thank you.

2 Answers

I mean, can I get into troubles with the second way?

No. You're much more likely to get into trouble with A (using useCallback for a function you later use in useEffect). You're not at all likely to get into trouble with B (using functional updates).


Beware that useCallback (like useMemo) doesn't guarantee that it won't recreate the function if the dependencies don't change. It's a performance optimization, not a semantic guarantee. Your combination of useCallback and useEffect may cause the effect function to run even when nothing other than the callback has changed, and changed simply because React decided to forget the old version. See the warning on useMemo:

You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

(their emphasis)

This applies to useCallback too, because as they say at the very end of the useCallback documentation:

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

Using the first approach you will be creating a different callback every time carData changes, which in turn rerenders any hook/component that you pass the callback as a prop.

A better approach would be to utilize setCarData implicit prevState to avoid such unnecessary rerender.

Related