="gist-iframe" src="/gist/nivedhasamy/f5dd10495398b89e893cb4b9d9b65aac.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="cfe0">This basically replicated the <code><i>componentDidUpdate</i></code> lifecycle method.</p><p id="f511">We can also pass multiple values to the dependency array.</p><p id="cd6e">A classic counter example will help us better understand this:</p>
<figure id="63a5">
<div>
<div>
<img class="ratio" src="http://placehold.it/16x9">
<iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodesandbox.io%2Fembed%2F69kbw&display_name=CodeSandbox&url=https%3A%2F%2Fcodesandbox.io%2Fs%2F69kbw&image=https%3A%2F%2Fcodesandbox.io%2Fapi%2Fv1%2Fsandboxes%2F69kbw%2Fscreenshot.png&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codesandbox" allowfullscreen="" frameborder="0" height="500" width="1000">
</div>
</div>
</figure></iframe></div></div></figure><p id="edc0">In the example above, an update in <code>counter1</code> or <code>counter2</code> would trigger the below effect.</p>
<figure id="4e78">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/535c40873689008e8570f010f3c5781b.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><h2 id="24b8">Passing objects in dependency array</h2><p id="bc79">Now, what if your callback is dependent on an object. Would our effects run successfully if you do this?:</p>
<figure id="0c73">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/2956ee9a1b607cac5d9ae0bf215b0d14.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="28af">The answer is no — because objects are reference types. Any change in the object’s property would go unnoticed by the dependency array because only the reference is checked but not the values inside.</p><p id="363a">There are a few approaches that you can follow to perform deep comparisons in objects.</p><ul><li><code>JSON.stringify</code> the object:</li></ul>
<figure id="c3af">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/f6d25bf6e5cf102e5243c44c4e1b44be.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="32e2">Now, our effect could detect when an object’s property changes and function as expected.</p><ul><li><code>useRef</code> and Lodash for comparison:</li></ul><p id="3786">You can also write your own custom functions for comparison using <code>useRef</code>. It is used to hold mutable values throughout the component's lifetime in its <code>current</code> property.</p>
<figure id="a5db">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/04854db76be4203f76adb6efe373b190.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="2882">You can read more on <a href="https://reactjs.org/docs/hooks-reference.html#useref">useRef</a> if you’re not familiar with it.</p><ul><li>External packages</li></ul><p id="8fe2">If your object is too complex to do a comparison yourself, you can use npm packages to do it for you. A popular suggestion is <a href="https://www.npmjs.com/package/use-deep-compare-effect"><code>use-deep-compare-eff</code>ect</a>:</p>
<figure id="0eeb">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/acddf1f77a3497db79dc193c2f8d1adb.js" allowfullscreen="" framebo
Options
rder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="1a8c">The <code>useDeepCompareEffect</code> will do a deep comparison and run the callback only when the object <code>obj</code> has changed.</p><h1 id="0064">3. Multiple useEffect for Cleaner Code</h1><p id="7416">Now that we know about the dependency array, we might need to separate effects to run on different lifecycle events of a component or simply for cleaner code.</p><p id="cdba">As already seen in the <a href="https://codesandbox.io/s/snowy-flower-9elps?file=/src/App.js">demo</a> earlier, it is possible to write multiple <code>useEffect</code> in a single component.</p>
<figure id="f29c">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/600911b6fe617b991a5e71814b172dc8.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><h1 id="4e03">4. Handle Dependencies in Functions</h1><p id="c82d">Suppose you want to break down the code into smaller functions and call it from effect, as below:</p>
<figure id="9725">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/3a2a1a7539e569907651ae430b53bf4e.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="4105">This would not give us the expected behaviour — <code>doSomething</code> is dependent on <code>data</code> which is not included in the <code>useEffect</code> array. Any update to data will not fire our callback.</p><p id="8d6a">This is why React recommends we have the function within the <code>useEffect</code> as its easier to trace the dependencies.</p><p id="81ad">But what if you want to write reusable functions? Or pass it down from a parent component?</p><p id="f964">When you want to pass a function as a prop to its child component, you need to add the function to the dependency array in the child component’s effect. But every time something changes in parent, new instances of these functions are created and our callback is fired. This is inefficient.</p><p id="3763">For scenarios like these, <code>useCallback</code><b> </b>comes to the rescue.</p>
<figure id="e3a8">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/e3066b9b0a98b3d3d2f65ebad95e61fc.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="3b2b">Similar to <code>useEffect</code>, the <a href="https://reactjs.org/docs/hooks-reference.html#usecallback"><code>useCallb</code>ack </a>accepts a callback and an array of dependencies. It will return a memoized version of the callback that only changes its identity if any of the dependencies has changed, ensuring we don't create a new instance of the function every time the parent re-renders.</p><p id="5962">You can experiment with the above <a href="https://codesandbox.io/s/currying-shape-2dv3l?file=/src/App.js">here</a>.</p><h1 id="59f7">5. Always Call Hooks at the Top Level</h1><p id="7a2a">You cannot call <code>useEffect</code>(or any Hooks) inside conditional, loops or nested functions.</p>
<figure id="362c">
<div>
<div>
<iframe class="gist-iframe" src="/gist/nivedhasamy/69faa11703b7aaa54fa028dc7e54485f.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="5044">You can read more on <a href="https://reactjs.org/docs/hooks-rules.html">rules of Hooks</a>.</p><h1 id="b30b">TL;DR</h1><p id="5b2e">All the points discussed above are in <a href="https://codesandbox.io/s/cool-cloud-jmlrx?file=/src/App.js">this</a> code. You can play around with it.</p><p id="ffb8">Thanks for reading. Happy coding!</p></article></body>
Tips for Using React’s UseEffect Effectively
Dependency array, code optimization, useCallback, and more
Hooks are a great way to access React’s state and lifecycle methods from a functional component. The useEffect Hook is a function (effect) that runs after render and every time the DOM updates.
In this article, we’ll discuss some tips to better use the useEffect Hook.
1. Child Effects Fire First
Think of the useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
So the useEffect Hook behaves similarly to the class lifecycle methods. One behaviour to note is that the child callback is fired prior to the parent callback. You can check this behaviour here.
Say you have to trigger a payment automatically. This code is written in the child component that runs after render, but the actual details (total amount, discount, etc.) required for payment is fetched in the parent component’s effect. In this case, as payment is triggered before setting the details required, it won’t be successful.
Bear this in mind and structure your code accordingly.
2. The Dependency Array
Let’s start with the basics. The useEffect Hook accepts a second argument, known as a dependency array, to control when the callback should fire.
Running effects on every DOM update
Not passing in a dependency array will run the callback on every DOM update.
Running effects on initial render
Passing in an empty array runs the effects only after the initial render. By this time the state is updated with its initial values. Further updates in DOM does not call this effect.
This is similar to the componentDidMount and componentWillUnmount (on return) lifecycle methods.
This is where all the listeners and subscriptions required for your page is added.
Running effects on specific props changes
Suppose you have to fetch data (product details) based on which product a user is interested in — the selected product has a productId, for example. We need to run the callback every time the productId changes — not just on the initial render or on every DOM update.
This basically replicated the componentDidUpdate lifecycle method.
We can also pass multiple values to the dependency array.
A classic counter example will help us better understand this:
In the example above, an update in counter1 or counter2 would trigger the below effect.
Passing objects in dependency array
Now, what if your callback is dependent on an object. Would our effects run successfully if you do this?:
The answer is no — because objects are reference types. Any change in the object’s property would go unnoticed by the dependency array because only the reference is checked but not the values inside.
There are a few approaches that you can follow to perform deep comparisons in objects.
JSON.stringify the object:
Now, our effect could detect when an object’s property changes and function as expected.
useRef and Lodash for comparison:
You can also write your own custom functions for comparison using useRef. It is used to hold mutable values throughout the component's lifetime in its current property.
You can read more on useRef if you’re not familiar with it.
External packages
If your object is too complex to do a comparison yourself, you can use npm packages to do it for you. A popular suggestion is use-deep-compare-effect:
The useDeepCompareEffect will do a deep comparison and run the callback only when the object obj has changed.
3. Multiple useEffect for Cleaner Code
Now that we know about the dependency array, we might need to separate effects to run on different lifecycle events of a component or simply for cleaner code.
As already seen in the demo earlier, it is possible to write multiple useEffect in a single component.
4. Handle Dependencies in Functions
Suppose you want to break down the code into smaller functions and call it from effect, as below:
This would not give us the expected behaviour — doSomething is dependent on data which is not included in the useEffect array. Any update to data will not fire our callback.
This is why React recommends we have the function within the useEffect as its easier to trace the dependencies.
But what if you want to write reusable functions? Or pass it down from a parent component?
When you want to pass a function as a prop to its child component, you need to add the function to the dependency array in the child component’s effect. But every time something changes in parent, new instances of these functions are created and our callback is fired. This is inefficient.
For scenarios like these, useCallbackcomes to the rescue.
Similar to useEffect, the useCallback accepts a callback and an array of dependencies. It will return a memoized version of the callback that only changes its identity if any of the dependencies has changed, ensuring we don't create a new instance of the function every time the parent re-renders.