DEV Community

Cover image for Using UseEffect Hook: Avoiding Excessive Re-rendering
Ananna Dristy
Ananna Dristy

Posted on

Using UseEffect Hook: Avoiding Excessive Re-rendering

React is one of the most popular front-end web development frameworks and has gained widespread adoption since its release in 2013. In React, there are two ways to define a component: class components and functional components. Functional components are widely used now-a-days because they are simpler and more lightweight than class components. To manage states and side effects, hooks are used in functional component which have made our works much easier.

UseEffect is one of the react hooks that allows functional components to perform side effects, such as fetching data or updating the DOM, in response to changes in state or props. But for the beginners, it may be somewhat troublesome. There are some common mistakes while using useEffect in code.

I have assumed that people reading this blog have already used useEffect hooks before. One of the simplest examples for showing useEffect hook is of increasing counter value and displaying the value in the webpage.

import {useEffect, useState} from "react";

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

  useEffect(()=>
  {
    console.log("Rerendering!")

    setInterval(() => {
      setCount(count+1)
    },1000)
  },[count])

  return (
  <div>Current count is: {count}</div>
  )
}

export default App;

Enter fullscreen mode Exit fullscreen mode

We have used the useState hook to define a state variable called count and a function called setCount which will be used to update the state of count. In this case, the initial value of count is set to 0.

The useEffect hook is used to handle side effects in functional components. In this case, it sets an interval using setInterval to update the count value every 1000ms (1 second). Whenever the count value changes, the useEffect hook is called again due to the dependency array [count], and the component is re-rendered.

You can find the running code here.

After running the code, you will observe some abnormalities in the webpage. You will understand better if you open your console. It have been rendering infinitely. This has happened because the count is updating every second. Since the count is the dependency in our useEffect, it runs the function again and again. 

But a question can appear in your mind, why the number of Rendering printed on the console is more than the count. As we already know the function is run when the count is changed. This happens because the setInterval function is asynchronous, it is possible for multiple updates to be scheduled before the component has a chance to re-render. This means that there may be several updates to the count state variable before the component actually re-renders and displays the updated count. This is why you may see more log messages for "Rendering!" than the count updates you observe.

Now, to remove this infinite loop, we will now update the code a bit.

import { useEffect, useState } from "react";

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

  useEffect(() => {
    console.log("Rerendering!");

    setInterval(() => {
      setCount((prev) => prev + 1);
    }, 1000);
  }, []);

  return <div>Current count is: {count}</div>;
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Here, we pass an empty dependency array [] to the useEffect hook, which means that the effect will only run once on mount and not re-run on updates. We also use a functional update to update the count state variable inside the setInterval function, which ensures that we always have the latest value of count and avoids the infinite loop.
You can see the running code here.

This time the code runs almost perfectly. The re-rendering is seen on the log once or twice. It's supposed to be one. But because your App is wrapped inside React.StrictMode, it renders twice. StrictMode renders components twice (on dev but not production) in order to detect any problems with your code and warn you about them (which can be quite useful). So, you may get 2 log messages of Rerendering!

index.js

Now, it is the time for making this piece of code perfect as we have said the previous one was almost perfect. The best practice of using useEffect is with a cleanup function.

import { useEffect, useState } from "react";

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

  useEffect(() => {
    console.log("Rerendering!");

    const interval = setInterval(() => {
      setCount((prev) => prev + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []);

  return <div>Current count is: {count}</div>;
}

export default App;

Enter fullscreen mode Exit fullscreen mode

In this code, the clearInterval function is called inside the cleanup function that is returned from the useEffect hook. You can find the running code here.

The useEffect hook sets an interval using the setInterval function, which repeatedly updates the count state variable. Without a cleanup function, the interval would continue running even after the component is unmounted, leading to potential memory leaks and performance issues. The cleanup function ensures that the interval is cleared when the component is unmounted or when the count state variable changes and the effect is re-run.

Hope that now you all can write useEffect hooks without any confusion of excessive re-rendering. This is all for now. For any enquiries, let me know.

Top comments (0)