REACT Lifecycle with useEffect

React’s useEffect Hook provides a way to handle lifecycle events in functional components, replacing the need for lifecycle methods found in class components. It allows developers to execute side effects in different stages of a component’s lifecycle: mounting, updating, and unmounting.

How useEffect Works

The useEffect Hook takes two arguments:

  1. Effect Function: A function containing the logic for the side effect.
  2. Dependency Array (optional): A list of values that the effect depends on. React re-runs the effect only when one of these values changes.
    useEffect(() => {
      // Your effect logic here
    }, [dependencies]);
    

    Try It Now

Mapping useEffect to Lifecycle Events

  1. Mounting: When the component is first rendered.
  2. Updating: When the component’s state or props change.
  3. Unmounting: When the component is removed from the DOM.

 

1. Mounting (Equivalent to componentDidMount)

To run an effect only once after the component mounts, provide an empty dependency array [].

import React, { useEffect } from "react";

const App = () => {
  useEffect(() => {
    console.log("Component mounted");

    // Example: Fetch data
    fetch("/api/data")
      .then(response => response.json())
      .then(data => console.log(data));
  }, []); // Empty dependency array ensures this runs only once

  return <h1>Hello, React!</h1>;
};

export default App;

Try It Now

 

2. Updating (Equivalent to componentDidUpdate)

To run an effect when specific dependencies change, include those dependencies in the array.

import React, { useState, useEffect } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`Count updated: ${count}`);
  }, [count]); // Runs only when 'count' changes

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;

Try It Now

3. Unmounting (Equivalent to componentWillUnmount)

To clean up resources when a component unmounts, return a cleanup function from the useEffect.

import React, { useEffect } from "react";

const Timer = () => {
  useEffect(() => {
    const interval = setInterval(() => {
      console.log("Tick");
    }, 1000);

    // Cleanup function
    return () => {
      clearInterval(interval);
      console.log("Component unmounted");
    };
  }, []); // Empty dependency array ensures this runs once

  return <h1>Timer is running...</h1>;
};

export default Timer;

Try It Now

Handling All Lifecycle Stages

useEffect can handle mounting, updating, and unmounting in a single declaration.

import React, { useState, useEffect } from "react";

const LifecycleDemo = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("Mounting or updating...");

    return () => {
      console.log("Cleanup before unmount or re-run of effect");
    };
  }, [count]); // Runs on mount and every time 'count' changes

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default LifecycleDemo;

Try It Now

Best Practices for Using useEffect

  1. Manage Dependencies Carefully:
    • Always specify dependencies in the array to avoid unnecessary re-renders or missed updates.
    • Use the eslint-plugin-react-hooks rule to catch common dependency issues.
  2. Avoid Heavy Computations:
    • Use memoization (useMemo or useCallback) to prevent re-executing expensive operations.
  3. Separate Concerns:
    • Avoid putting too much logic in a single useEffect.
    • Use multiple useEffect calls to separate concerns, e.g., data fetching, subscriptions, or event listeners.
      useEffect(() => {
        // Data fetching logic
      }, [dataDependency]);
      
      useEffect(() => {
        // Event listener logic
      }, []);
      

      Try It Now

  1. Cleanup Properly:
    • Always return a cleanup function to avoid memory leaks (e.g., remove event listeners, cancel subscriptions).
  2. Use Custom Hooks:
    • Abstract common logic into reusable custom hooks for cleaner code.

Comparison: Class vs. Functional Components

Lifecycle Event Class Component Functional Component (useEffect)
Component Mount componentDidMount useEffect(() => { ... }, [])
Component Update componentDidUpdate useEffect(() => { ... }, [dependencies])
Component Unmount componentWillUnmount useEffect(() => { return () => { ... }; }, [])