Search code examples
reactjsreact-hooksuse-effect

Why react function reference doesn't change at every render?


React learner here.

I create a new react redux typescript app:

npx create-react-app my-app --template redux-typescript

In the sample code, I add an useEffect to dispatch an action only one time the first time the component is rendered:

in src\features\counter\Counter.tsx

   const dispatch = useAppDispatch();
   
   ...
 
  useEffect(() => {
     dispatch(increment())
  }, [dispatch]);

And I also add another button to change a new state in this component to force a new rendering of the component and be sure that useEffect code is called just one time.

The problem is that everything works but I don't understand one thing.

I learn (for sure I'm missing something) that upon each render cycle, "dispatch" function should be redefined. So each render cycle "dispatch" should have a different reference and this should trigger useEffect again.

But this is not was is happening.

Looks like that at each render "dispatch" don't change reference (otherwise useEffect will be called again), but how this is possible? What's the point I'm missing?

I was confused because If I create a custom hook like this:

const useRequestHook = () => {
 
  const sendRequest =useCallback(() => {
      console.log("send request invoked")
  }, []);

  return sendRequest;

};

export default useRequestHook;

And if I use it in my Counter component:


      const sendRequest = useRequestHook();
    
      useEffect(() => {
        sendRequest();
      }, [sendRequest]);

I have issues when component renders again for example when I click the button. Because the new render will change sendRequest reference and useEffect will be called again, so the solution is to wrap in the useCallback

 

     import { useCallback } from "react";
    
    const useRequestHook = () => {
     
      const sendRequest =useCallback(() => {
          console.log("send request invoked")
      }, []);
    
      return sendRequest;
    
    };
    
    export default useRequestHook;

Now it worked as expected and as I understand.

Why the behavior is different between "dispatch" and "sendRequest"?


Solution

  • Docs: https://react-redux.js.org/api/hooks#usedispatch

    This hook returns a reference to the dispatch function from the Redux store.

    This is the key. With the custom hook useAppDispatch you are actually getting a reference to the dispatch function so you are not recreating the function each time, and that is why useEffect does not get called on each re-render.