5 Things You Should Know About useEffect

5 Things You Should Know About useEffect

Hey, devs👋 how are you folks doing? Today's article is all about the useEffect hook🪝in React.js. We'll discuss the useEffect hook in detail and a few important things that you should keep in mind while using it.

Let's get right into it, and see how to use useEffect.

1. Why do we need useEffect (Intro)?

The main task of React is to render and re-render the UI according to the user input. Basically, 'react' to user inputs. To achieve this behaviour we make use of different features of React.js like props, useState, and useRef.

However, these features should not be used to handle 'side-effects' in your applications. Now, what is a side-effect? Sending an HTTP request to fetch or post data, saving something to local storage, and setting or managing timers, all these tasks are side effects.

Now, why do you have to use useEffect to manage side effects in your function components? A normal function component that uses useState in it will get re-rendered every time there is a change in the state of the component.

Now, if we make a side-effect action that changes the state of the component. The useState will re-render the UI and the side effect will run again. That'll again change the state and it'll trigger the useState to re-render the UI, and this will create an infinite loop.

Don't worry we have useEffect to save us! We put our side-effect code in the callback function of the useEffect and an array of dependencies as the second argument. This callback function of the useEffect only runs when the elements in the array of dependencies change and the first time the component is rendered. Hence, the side-effect code will only run when anything in the dependency array changes. We are safe now from the infinite loop😌

Syntax

useEffect(()=>{
    callbackFunction
}, [dependencyArray])

2. When does useEffect's callback function run?

Whenever the useEffect's callback function runs, it runs after every other function and line of code in the component is done executing.

The useEffect function always runs the first time, when the component gets mounted(irrespective of the dependency array). After that, the function will only run the second time and thereafter if anything in the array of dependencies changes. The useEffect function doesn't care about state changes unless the state is one of its dependencies.

3. What should you add to the dependencies array?

Using useEffect's dependencies you can re-run the logic inside the callback function of useEffect. Every time anything in the dependency array changes, the useEffect function will run.

You have to add all the variables and functions you use inside the useEffect's callback function to the dependencies array except, the state updating function of useState(), built-in APIs(fetch, localStorage), and functions or variables defined outside the functions.

  const [enteredEmail, setEnteredEmail] = useState("");
  const [enteredPassword, setEnteredPassword] = useState("");
  const [formIsValid, setFormIsValid] = useState(false);

useEffect(() => {
    setFormIsValid(
      enteredEmail.includes("@") && enteredPassword.trim().length > 6
    );
  }, [enteredEmail, enteredPassword]);

// No need to add setFormIsValid() in the array, because it's a state updating function.

// We add enteredEmail and enteredPassword because they are used inside the callback function and we want to execute the useEffect everytime they change.

There are two important cases you should know about:

  • The useEffect function runs every time:

    If you want the useEffect() hook to run every time the component renders or re-renders, you don't add the dependency array at all.

      useEffect(()=>{
          .....
          ....some task
      })
    
      // No second argument
    
  • The useEffect function only runs once:

    If you want the useEffect() hook to only run when the component gets mounted(the first time), then you add an empty array of dependencies.

      useEffect(()=>{
          .....
          ....some task
      }, [])
    
      // Empty dependencies array
    

4. The cleanup function

Now we are getting into good stuff here! The useEffect hook can also return a function in the callback, this function is known as the cleanup function.

useEffect(()=>{
    .....
    ...some task
    return function xyz(){  // cleanup function
        .....
        ...some task
    }
}, [dependencies])

🚨The cycle of the useEffect hook

This is one of the most important concepts of the useEffect hook and it's also the most misunderstood one. Here, you'll see the life cycle of a useEffect hook with a cleanup function.

  1. When the component gets mounted(it's rendered for the first time) the callback function is executed. However, the cleanup function is not executed the first time.

  2. On later renderings, before invoking the next side-effect callback function, useEffect() hook execute the cleanup function from the previous side-effect execution (to clean up everything after the previous side-effect), then runs the current side-effect callback function. Wait, read that again, let that sink in! It's a very important concept.

  3. And finally, when unmounting the component the useEffect() hook invokes the cleanup function from the latest callback function of useEffect.

5. The useEffect hook is not just for side effects

It's not like the useEffect hook is exclusive for handling side-effects. You can do much more with it. See, the main task of the useEffect() is to run a block of code independent of the render cycle of React.

Hence, if there is any logic in your application that you only want to run when a certain state or variable changes, you can go with useEffect hook.

6. Bonus🤑

Here is a little reward for you for reaching this far. The callback function of the useEffect hook cannot be asynchronous.

So, when you are making an HTTP request using the fetch API inside the callback function, you can't make the callback function async . Don't panic, there is a workaround.

There are no restrictions in using an async function inside the callback function of the useEffect() . Create an async function inside the callback and call the fetch API inside that asynchronous function.

// Can't Do!
useEffect(async()=>{
    const res = await fetch();
});

// Can Do!
useEffect(()=>{
    const fetchFun = async()=>{
        const res = await fetch();
    }
    fetchFun();
});

Conclusion

And that's all from my side! Hope you enjoyed reading it and learned something. Thank you for reading, do share your thoughts about useEffect and what you like or dislike about it in the comment section. You can follow me on Twitter at @muditwt and subscribe to my newsletter to get the latest articles from me in your inbox📩

Did you find this article valuable?

Support Mudit Mishra by becoming a sponsor. Any amount is appreciated!