useEffect Hook ?

useEffect Hook ?

In this article we will learn about useEffect Hook

What is useEffect Hook

The useEffect() hook allows us to run side effects on your functional components.

what is a side effect?

side effect means any operations that affect something outside the component, such as fetching data from an API, manually manipulating the DOM, timers etc. It will run after the component is rendered.

The useEffect hook accepts two arguments: a callback function and an optional array of dependencies. The callback function contains the side effect logic, and it will be executed after every render.

syntax

// Syntax of useEffect hook:
useEffect(callback, [dependencies]);

function App() {
  // Use useEffect hook:
  useEffect(() => {
    // Execute some code.
  }, [])
  // ...
}

useEffect dependency

The second argument of useEffect allows you to specify the dependencies for the effect. Depending on the value of the second argument, you have three options:

  1. No Dependencies ([] or no second argument): When you provide an empty array [] or omit the second argument altogether, the effect runs only once after the initial render. It is equivalent to the behavior of componentDidMount in class components. This is useful when you want the effect to execute some initialization logic that doesn't depend on any specific props or state.

    Example:

     useEffect(() => {
       // Initialization logic
     }, []);
    
  2. Specific Dependencies: You can provide an array of dependencies as the second argument. The effect will be re-run whenever any of the dependencies change. This is useful when you want to perform the effect only when certain values have changed.

    Example:

     useEffect(() => {
       // Effect logic
     }, [dependency1, dependency2]);
    
  3. No Second Argument: If you omit the second argument entirely, the effect will run after every render. This is equivalent to providing no dependencies, but it can be less efficient because the effect will execute even if the props or state haven't changed.

    Example:

     useEffect(() => {
       // Effect logic
     });
    

Example (Passing arguments in dependency array):

import { useEffect, useState } from "react";
import "./App.css"

const App = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(counter + 1);
    }, 1000);

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

  return (
    <div>
      <h1>Counter</h1>
      <p>{counter}</p>
    </div>
  );
};

export default App

In this example, we have a component named App. It uses the useState hook to define a state variable counter with an initial value of 0. It also imports the useEffect hook from the React library.

The useEffect hook is then used to manage side effects in the component. It takes two arguments: a callback function and a dependency array. The callback function is executed after the component renders, and it can perform any necessary side effects.

In this case, the callback function sets up an interval using setInterval, which increments the counter value by 1 every second. The counter value is updated using the setCounter function.

The dependency array [counter] specifies that the effect should re-run whenever the counter value changes. This ensures that the interval is cleared and restarted whenever the counter value updates, preventing memory leaks.

Additionally, the useEffect hook also returns a cleanup function that clears the interval using clearInterval when the component unmounts. This helps to clean up any resources or subscriptions created by the effect.

Finally, the component renders a <div> containing an <h1> element with the text "Counter" and a <p> element that displays the current value of the counter state.

Example (Passing an empty dependency array):

import { useEffect, useState } from "react";
import "./App.css"

const App = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(counter + 1);
    }, 1000);

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

  return (
    <div>
      <h1>Counter</h1>
      <p>{counter}</p>
    </div>
  );
};

export default App

In this case, the effect is responsible for starting an interval that increments the counter value every second. Since the dependency array is empty, the effect only runs once, after the initial render.

By returning a cleanup function from the effect, the interval is cleared when the component unmounts, ensuring that there are no memory leaks.

The component renders a <div> element containing an <h1> element with the text "Counter" and a <p> element that displays the current value of the counter state.

Overall, this usage of useEffect with an empty dependency array is useful when you want the effect to run only once during the lifecycle of the component, such as for setting up event listeners or fetching data on component mount.

Example (Without Passing dependency array):

import { useEffect, useState } from "react";
import "./App.css"

const App = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(counter + 1);
    }, 1000);

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

  return (
    <div>
      <h1>Counter</h1>
      <p>{counter}</p>
    </div>
  );
};

export default App

In this case, the effect is responsible for starting an interval that increments the counter value every second. Since no dependency array is provided, the effect will be triggered after every render of the component, causing a new interval to be created on each render.

However, since the counter state is used within the effect, it creates a closure and captures the initial value of counter when the effect is defined. This means that even though the counter state updates, the effect will continue to use the initial value within its closure.