DEV Community

Cover image for 5 Common Mistakes When Using useEffect Hook in React
Muslimat Mojeed
Muslimat Mojeed

Posted on

5 Common Mistakes When Using useEffect Hook in React

Introduction

Errors are inevitable in coding but can be avoided or managed. Bugs can be easily identified when encountered with a proper understanding of good coding practices.
One aspect where errors often arise is when using the useEffect Hook, typically due to mistakes made by some developers.
In this article, I will briefly discuss what useEffect entails, the common mistakes and best practices, and when to use the useEffect Hook.
You should be familiar with React and useEffect Hooks before reading this article. Whether you're just starting with React Hooks or you've been using them for a while, this article is for you.

What is useEffect?
useEffect(function () { }, []);

useEffect is a hook that allows us to handle a side effect.
Literally, side effects involve interactions between a React component and the world outside the component.

For example, fetching data from a server into your component is a side effect because there's an interaction between the server and the React component. The useEffect Hook serves as a connection between the two, allowing us to manage side effects efficiently.

It is a great Hook that helps prevent infinite re-fetching, ensuring data is fetched once after the component mounts.

Definition of Key Terms

  • Dependency Array ([]): This is an empty array that ensures fetched data runs only when the component renders for the first time. Infinite loops is halted using the dependency array.
  • Cleanup Function: In React, a cleanup function helps manage side effects effectively. The cleanup function resets a state/element back to its previous state, allowing developers to clean up resources such as canceling API requests, clearing timers, or removing event listeners. This prevents the persistence of certain actions and ensures optimal performance.

5 Common Mistakes

1.Missing Dependency Array:
Without a dependency array, data will be re-fetched after every render. HTTP requests will be sent to the server on every render, causing an infinite loop.
Example

Import {useEffect} from 'react';
function App(){
//Incorrect 
 useEffect(function () {
//Your Logic here }) 

//Correct 
useEffect(function () {
//Your Logic here }, [])
return <p> Hey Effects</p>;
}
export default App;

Enter fullscreen mode Exit fullscreen mode

2.Incorrect or Missing Properties in the Dependency Array:
React would not be aware of any state variable or prop used inside an effect if it is not included in the dependency array. This could lead to a bug called "stale closure".

Stale Closure: A stale closure happens when a function remembers old values from its surrounding code instead of the most recent ones. Imagine React remembering the initial state or props but failing to recognize updates after each state change.
Example

//Incorrect
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    //Your logic here
  }, []);

 return <button>Increment</button>;
}

Enter fullscreen mode Exit fullscreen mode
//correct
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    //Your logic here
  }, [count]);

 return <button>Increment</button>;
}

Enter fullscreen mode Exit fullscreen mode

3.Overusing an Effect:
Writing excessive logic within a single useEffect is a bad practice.

Additionally, the useEffect Hook has its specific purpose in React. Using useEffect for actions that can be handled manually or with other hooks is considered a bad practice.

Overusing it can degrade performance, cause issues during cleanup, and introduce unnecessary bugs that might be difficult to identify.

Example

import { useState, useEffect } from 'react';

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

  // Incorrect
  useEffect(() => {
    // Fetching data
    fetch('/api/data')
      .then(response => response.json())
      .then(data => setData(data));

    // Setting up a timer
    const timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);

    // Cleanup function
    return () => clearInterval(timer);
  }, []);

  return <div>{ data and count}</div>;
}
Enter fullscreen mode Exit fullscreen mode
//Correct
import { useState, useEffect } from 'react';

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

  // Fetching data
  useEffect(() => {
    fetch('/api/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  // Setting up a timer
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);

    return () => clearInterval(timer);
  }, []);

  return <div>{data and count }</div>;
}


Enter fullscreen mode Exit fullscreen mode

4.Not Cleaning Up Effects:
Side effects (e.g., fetching data from an API) could persist after the component has been re-rendered or unmounted, causing performance issues like memory leaks or unnecessary data fetching.

//Incorrect
import { useState, useEffect } from 'react';

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

  // Setting up a timer
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);
}, []);

 return <div>{ data and count 
}</div>;
}
Enter fullscreen mode Exit fullscreen mode
//Correct
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);

//Clean-up function 
    return () => clearInterval(timer);
  }, []);

 return <div>{data and count}</div>;
}

Enter fullscreen mode Exit fullscreen mode

5.Storing useEffect in a Variable:
Storing a useEffect function in a variable can lead to loss of effect execution and stale closure.

If you store the effect function in a variable without calling it inside the useEffect, the side effect will not execute, leading to unintended behavior in your component.

Example

import { useEffect } from 'react';

function App() {
  // Incorrect 
 const effectData = function () {
       //Your logic here
     };

  useEffect(effectData
, []);

  return <div>Data...</div>;
}
Enter fullscreen mode Exit fullscreen mode
//Correct
import { useEffect } from 'react';

function App() {
  useEffect(function () { 
       //Your Logic here
     }, []);

 return <div>Data..</div>;
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Always add the dependency array to avoid infinite loops.
  • Add state or props you're currently using within the effect to the dependency array to avoid stale closure.
  • Avoid writing excessive logic within a single useEffect. Use multiple hooks to maintain good practices.
  • Use the useEffect Hook for its intended purpose such as setting up timers, subscriptions, fetching data, etc.
  • Ensure you clean up functions to avoid memory leaks or performance-related issues.
  • Don't store useEffect or effect functions in a variable.

Final Thoughts

Being mindful of these mistakes and following good practices while coding prevent unnecessary errors that could delay productivity.
This article has explored the common mistakes in useEffect and its best practices.
I hope you found the article insightful.
Have you encountered any mistakes aside from the ones mentioned above? Drop them in the comment box!

Top comments (0)