DEV Community

ali
ali

Posted on • Edited on

useEffect : A simple explanation ❀

Hi all πŸ‘‹
React Hooks are an integral part of react and is easy to get confused with their usage. Today I will explain one of the common React hook used, i.e useEffect().

React documentation defines that

The useEffect accepts a function that contains imperative, possibly effectful code.

Let's take an example

Imagine a scenario where you are opening a web app displaying all the recent news. Now when you are opening the application, you aren't triggering any action. You aren't clicking any button or entering any data. But the app automatically fetches the news for you. This is a simple example where you would use the useEffect hook. A useEffect hook can be used for data fetching, setting up a subscription, and manually changing the DOM in React components (usually called as side effects). We can also control the execution of the same.

Let me start with the syntax πŸ±β€πŸ’»

The syntax

Basically useEffect takes in two parameters. Effect and an optional dependencies.

  • Effect is an imperative function that can return a cleanup function. ( )
  • Dependency is an array which if present, will enable the hook to run only if the values in the list change. πŸ“ƒ

So the basic syntax of useEffect will be as shown below.

        useEffect(effect,dependency);
Enter fullscreen mode Exit fullscreen mode

Do remember that the dependency is an optional parameter. So it is possible to use useEffect as shown below.

        useEffect(effect);
Enter fullscreen mode Exit fullscreen mode

What is an effect ?

As stated above , it as a function. Lets see the following example.

        useEffect(() => console.log('Inside useEffect'));
Enter fullscreen mode Exit fullscreen mode

Can you guess the number of times the console.log('') is gonna print. By default useEffect runs after the first render and after every update. So as long as there are re renders happening, this hook keeps on executing console log.

What is a dependency ?

The second parameter is an optional parameter which is an array. Now why do we need that ? Well remember how I told you that the useEffect runs on every re renders ? In order to restrict this , we can actually pass an array of dependencies.

Suppose you only want the useEffect to execute when you change a value, say a fruit.

        const [fruit,setFruit] = useState();

        useEffect(()=> {
            console.log('RENDER');
        })
Enter fullscreen mode Exit fullscreen mode

The above code would execute on every update, as useEffect runs on every change. To avoid this we could actually pass fruit as a dependency in the second argument as shown below.

        useEffect(()=> {
            console.log('RENDER');
        },[fruit])
Enter fullscreen mode Exit fullscreen mode

This would only run when the value of fruit changes. Easy right ?

Suppose you would only want to run some code , but only at the beginning, i.e when the component mounts. How do you achieve that ? Well its more easy, you only have to pass an empty dependency array as shown below.

        useEffect(()=> {
            setFruit(πŸ₯­);
        },[])
Enter fullscreen mode Exit fullscreen mode

I hope everything till now is clear...

useEffect with cleanup

While developing a react application, you might have come across an error called 'Memory Leak'. Well, this is where the cleanup function comes into the picture. I'll give you an example a developer can come across while developing a react app.

Suppose you are making an axios call. What would happen if you navigate to another route ? Ideally the axios call irrespective of it's status should cancel the request. But that wont happen automatically, rather would show you an error.

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Enter fullscreen mode Exit fullscreen mode

So as React suggests, we should cleanup the effect before the component unmounts. In class based components, we used to write this cleanup function in componentWillUnMount lifecycle method. In functional based component, we don't have such methods. So we write the cleanup code and return it as a function from useEffect

See the following example

const [fruit,setFruit] = useState(null);
useEffect(() => {
        let isMounted = true
        if(isMounted){
            setFruit(🍎);
        }
        return () => {
            isMounted = false
        }
    }, [])
Enter fullscreen mode Exit fullscreen mode

Ideally you should not update the state, when the component unmounts. But how do react know when to stop the state updation ? Notice the returning function from useEffect in the above code. This function tells react what to do, when the component unmounts which in our case is to stop updating our state. Here the value of isMounted is returned as false when the component unmounts, and we controll the flow using simple if condition.

Let me show you another example.

useEffect(() => {
        let source = axios.CancelToken.source();
        axios.get(url, {
            cancelToken: source.token,
        })
            .then(res => {
                console.log("GET REQUEST SUCCESS")
            }).catch((err) => {

                if (axios.isCancel(err)) {
                    console.log("GET REQUEST CANCELLED");
                } else {
                    console.log("GET REQUEST FAILED")
                }
            }
        });
        return () => {
            source.cancel('Operation canceled by the user.');
        };
    }, [url]);
Enter fullscreen mode Exit fullscreen mode

Axios provides a way to cancel a request using a cancel token Axios Cancellation. A cancel token can be created using the CancelToken.source factory as shown above. This token is then passed into the axios.get method. We also control the execution of the code by using simple if conditions as shown above.

You can also use the same with a post request.

axios.post(url, {
  userName: 'username',
  password: 'password'
}, {
  cancelToken: source.token
})
Enter fullscreen mode Exit fullscreen mode

It is also possible to pass in different values in the returning function of useEffect as shown below.

    return () => {
      isMounted = false;
      source.cancel('Operation canceled by the user.');
    };
Enter fullscreen mode Exit fullscreen mode

Similarly we can write all sort of code that should happen while the component unmounts.

A simple exercise using useEffect hook

Q. Increment a value in each second using useEffect code ⏱

import { useEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => setCount(count + 1), 600);
    return () => clearInterval(interval);
  },[count]);

  return (
    <div className="App">
      <h1> ⏱ = {count} </h1>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Notice how we have passed in count as a dependency. This would enable react to run the effect function as long as count changes. Thus the counter keeps on incrementing and updates the state. Also notice the returning function. The cleanup code will clear the interval using the clearInterval method.

Conclusion

To brief, useEffect hook accepts a function that contains imperative, possibly effectful code. You can control the execution using the second param which is dependency array. It is also important to write the cleanup code while working with useEffect using the return function. Hope I could explain each aspects of useEffect properly. Until next time. ❀

Top comments (2)

Collapse
 
aspraveen profile image
praveen

simple explanation!

Collapse
 
_ali_ profile image
ali

Glad i could help :)