👨🏼‍💻

khriztianmoreno's Blog

Home Tags About |

Posts with tag hooks

React useEffect

2022-09-05
javascriptreacthooks

useEffect is probably the most confusing and misunderstood hook in React. Today I want to clarify that for you.We use hooks all the time at Make It Real and understanding useEffect is crucial if we are going to write modern-style React code.Next, we will see:What is useEffect?How to run an effect on every renderHow to run an effect only on the first renderHow to run an effect on the first render and re-run it when a "dependency" changesHow to run an effect with cleanupWhat is useEffect?The useEffect hook allows us to perform side effects in our function components. Side effects are essentially anything where we want an "imperative" action to happen. This includes things like:API callsUpdating the DOMSubscribing to event listenersAll of these are side effects that we might need a component to perform at different times.Running useEffect on every renderThe useEffect hook does not return any value, but it takes two arguments. The first is mandatory and the second is optional. The first argument is the callback function of the effect we want the Hook to execute (i.e., the effect itself). Suppose we wanted to place a console.log() message inside the useEffect callback.import { useEffect } from "react"; export const FunctionComponent = () => { useEffect(() => { console.log("run for every component render"); }); return ( // ... ); }By default, the effect set in the useEffect hook runs when the component renders for the first time and after every update. If we run the above code, we will notice that the console.log('run for every component render') message is generated as our component renders. If our component ever re-renders (for example, from a state change with something like useState), the effect would run again.Sometimes, re-running an effect on every render is exactly what you want. But most of the time, you only want to run the effect in certain situations, such as on the first render.How to run useEffect only on the first renderThe second argument of the useEffect hook is optional and is a dependency list that allows us to tell React to skip applying the effect until certain conditions are met. In other words, the second argument of the useEffect hook allows us to limit when the effect will run. If we simply place an empty array as the second argument, this is how we tell React to only run the effect on the initial render.import { useEffect } from "react"; export const FunctionComponent = () => { useEffect(() => { console.log("run only for first component render (i.e., component mount)"); }, []); return ( // ... ); }With the above code, the console.log() message will only trigger when the component mounts for the first time and will not re-trigger, even if the component re-renders multiple times.This is much more "efficient" than running on every render, but isn't there a happy medium? What if we want to re-run the effect if something changes?Running useEffect on the first render and re-running it when the dependency changesInstead of making an effect run once at the beginning and on every update, we can try to restrict the effect to run only at the beginning and when a certain dependency changes.Suppose we wanted to trigger a console.log() message every time the value of a state property changes. We can achieve this by placing the state property as a dependency of the effect callback. See the following code example:import { useState, useEffect } from "react"; export const FunctionComponent = () => { const [count, setCount] = useState(0); useEffect(() => { console.log( "run for first component render and re-run when 'count' changes" ); }, [count]); return ( <button onClick={() => setCount(count + 1)}> Click to increment count and trigger effect </button> ); };Above, we have a button in the component template responsible for changing the value of the count state property when clicked. Each time the count state property changes (i.e., each time the button is clicked), we will notice that the effect callback runs and the console.log() message triggers.Running an effect with cleanupAn effect callback runs every time on the initial render and when we specify when an effect should run. The useEffect hook also provides the ability to run a cleanup after the effect. This can be done by specifying a return function at the end of our effect.import { useState, useEffect } from "react"; export const FunctionComponent = () => { const [count, setCount] = useState(0); useEffect(() => { console.log( "run for first component render and re-run when 'count' changes" ); return () => { console.log("run before the next effect and when component unmounts"); }; }, [count]); return ( <button onClick={() => setCount(count + 1)}> Click to increment count and trigger effect </button> ); };In the above example, we will notice that the cleanup function message triggers before the desired effect runs. Additionally, if our component ever unmounts, the cleanup function will also run.A good example of when we might need a cleanup is when we set up a subscription in our effect but want to remove the subscription whenever the next subscription call is to be made, to avoid memory leaks.These are mainly all the different ways the useEffect hook can be used to run side effects in components. I invite you to check out this visual guide to useEffect by ALEX SIDORENKO that illustrates these concepts through a series of GIFs that are both clever and effective, especially for visual learners. There is also a visualization of how first-class functions work if you want more.I hope this has been helpful and/or taught you something new!Profile@khriztianmoreno �