Skip to content
Advertisement

Different function gets passed to useEffect on every render? – ReactJS useEffect

I was going through useEffect from reactjs docs and I’ve found this statement here

Experienced JavaScript developers might notice that the function passed to useEffect is going to be different on every render.

We are passing a function to useEffect and this function is said to be different for each render. useEffect has access to state and props since it’s inside the function component and when either of these changes, we can see that change in the function of useEffect(because of closure) right? This is not clear, because in the next line the doc states

This is intentional. In fact, this is what lets us read the count value from inside the effect without worrying about it getting stale.

To counter this, assume we have a function

function foo(n) {
    bar = () => {
        setTimeout(() => console.log({n}), 50);
        return n;
    }
    setTimeout(() => {n = 10}, 0);
    setTimeout(() => {n = 20}, 100);
    setTimeout(() => {n = 30}, 150);
    return bar;
}

baz = foo(1);
baz(); //prints 10
setTimeout(baz, 300); //prints 30

It seems that when the closure value(n) is changed, we can see that change in the setTimeout’s callback (and this callback isn’t changed over time). So, how can the closured value(state/props) in useEffect’s function become stale as mentioned in docs?

Am I missing something here? I think it’s more of a JS question compared to React, so I took a JS example.

Advertisement

Answer

I found the answer a few days back, and as @apokryfos(Thank you again!) mentioned in the comments above, the program execution process is now making more sense. I want to summarize my learnings here.

Firstly, the code I considered, was not like with like comparison (in @apokryfos words) with the React doc statements, and this is true. In case of static HTML + vanilla JS where the HTML button has an event-listener JS function, this function is declared only once and when the event occurs the same JS function is executed everytime.

The code I have given in the question is similar to this, and so when executed in console or in event listener will only be declared once.

In case of React(or any state based UI libraries/frameworks), the HTML is not static and it needs to change on state-change. On the execution side (considering React), component will be created when we call it (in JSX), and the component’s function/class will be executed completely from top to bottom. This means

  • from all the event-handlers that doesn’t deal with state, constants to useState’s destructed elements and useEffect‘s callback functions, everything are re-initialized.

  • If the parent’s state changes initiate a render on its children(in normal scenarios), then the children will need to re-render themselves completely with the new props and/or new state to show the updated UI

  • Considering the example in React docs (link), useEffect with no dependencies will get executed after every render, and here it’s updating the DOM by showing the current state value. Unless the callback function has the latest value of that state, it’ll only print the stale value. So re-initialising the functions here is the main reason behind not having stale values in the callback functions

     function Example() {
         const [count, setCount] = useState(0);
    
         useEffect(() => {
         document.title = 'You clicked ${count} times';
         });
     }
    

This is a boon and a curse sometimes. Assume if we are sending useState’s setState function to the child, and in the child, we have a useEffect(that makes a network call to fetch data) that takes this function as its dependency. Now, for every state change in parent, even if there is a use-case or not, the useEffect will get triggered as this function in dependency is changed (as every update re-initializes the functions). To avoid this, we can utilize useCallback on the functions which we want to memorize and change only when certain params are changed, but it is not advisable to use this on useEffect‘s callback function since we might end-up in stale values.

Further Reading:

  1. GeeksForGeeks useCallback
  2. SourceCode interpretation of useEffect
  3. Another SourceCode interpretation of useEffect
Advertisement