Lesson 30 of 60 – useEffect Hook
50%

useEffect Hook

The useEffect Hook allows a React component to perform side effects. Side effects are operations that happen outside the normal process of calculating and rendering JSX, such as fetching data, changing the document title, setting up timers, or working with browser APIs.

Note: The useEffect Hook is commonly used when a component needs to synchronize with something outside the React rendering process.

1. What is useEffect?

useEffect is a React Hook used to perform side effects from a functional component.

import { useEffect } from "react";

function App() {

    useEffect(() => {

        console.log("Effect executed");

    });

    return (
        <h1>
            Hello React
        </h1>
    );

}

2. Importing useEffect

The Hook is imported from React.

import { useEffect } from "react";

If you use both useState and useEffect, both can be imported together.

import {
    useState,
    useEffect
} from "react";

3. Basic useEffect Syntax

The basic syntax is:

useEffect(() => {

    // side effect

});

The first argument is a function containing the effect logic.

4. useEffect After Rendering

An effect is run after React has rendered the component.

function App() {

    useEffect(() => {

        console.log("Component rendered");

    });

    return (
        <h1>
            Hello
        </h1>
    );

}

5. useEffect with Empty Dependency Array

An empty dependency array tells React that the effect does not depend on changing values from the component.

useEffect(() => {

    console.log("Effect executed");

}, []);

This pattern is commonly used for setup work when the component is mounted.

6. useEffect with a Dependency

A dependency can be placed inside the dependency array.

useEffect(() => {

    console.log("Count changed");

}, [count]);

The effect runs after rendering when the value of count changes.

7. useEffect with useState

import {
    useState,
    useEffect
} from "react";

function App() {

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

    useEffect(() => {

        console.log("Count:", count);

    }, [count]);

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

}

export default App;

8. Changing the Document Title

One simple example of an effect is changing the browser document title.

useEffect(() => {

    document.title = "React Tutorial";

}, []);

The browser tab title can be updated using the document.title property.

9. Updating Title with State

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

useEffect(() => {

    document.title = `Count: ${count}`;

}, [count]);

Whenever count changes, the document title is updated.

10. Dependency Array

The dependency array tells React which values the effect uses for synchronization.

useEffect(() => {

    console.log(name);

}, [name]);

When name changes, React runs the effect after the corresponding render.

11. Multiple Dependencies

More than one dependency can be specified.

useEffect(() => {

    console.log(name);
    console.log(age);

}, [name, age]);

The effect is synchronized when either dependency changes.

12. Effect Without Dependency Array

If no dependency array is provided, the effect runs after every completed render.

useEffect(() => {

    console.log("Effect executed");

});

This should be used carefully because frequent effects may perform unnecessary work.

13. Effect with Empty Dependency Array

An empty dependency array is written as [].

useEffect(() => {

    console.log("Initial effect");

}, []);

This is commonly used when the effect does not need to react to changing component values.

14. Effect for Data Fetching

A common use of useEffect is starting data fetching when a component needs external data.

useEffect(() => {

    fetch("https://example.com/api/users")
        .then(response => response.json())
        .then(data => {
            console.log(data);
        });

}, []);

15. Fetching Data and useState

Fetched data can be stored in state.

import {
    useState,
    useEffect
} from "react";

function App() {

    const [users, setUsers] = useState([]);

    useEffect(() => {

        fetch("https://example.com/api/users")
            .then(response => response.json())
            .then(data => {
                setUsers(data);
            });

    }, []);

    return (
        <div>

            {users.map(user => (
                <p key={user.id}>
                    {user.name}
                </p>
            ))}

        </div>
    );

}

16. useEffect and Loading State

A loading state can be used while asynchronous data is being retrieved.

const [loading, setLoading] =
    useState(true);

useEffect(() => {

    fetch("https://example.com/api/users")
        .then(response => response.json())
        .then(data => {

            setUsers(data);
            setLoading(false);

        });

}, []);

17. useEffect and Timers

Effects can be used to create timers or intervals.

useEffect(() => {

    const timer = setTimeout(() => {

        console.log("Timer completed");

    }, 2000);

}, []);

Timers created by an effect may need cleanup.

18. Cleanup Function

An effect can return a cleanup function. Cleanup is useful for removing subscriptions, timers, event listeners, and other resources created by the effect.

useEffect(() => {

    const timer = setInterval(() => {

        console.log("Running");

    }, 1000);

    return () => {

        clearInterval(timer);

    };

}, []);

19. Why Cleanup is Important

Cleanup prevents resources created by an effect from continuing after they are no longer needed.

Cleanup can be useful for:

  • Timers
  • Intervals
  • Event listeners
  • Subscriptions
  • Connections

20. Event Listener with useEffect

useEffect(() => {

    function handleResize() {

        console.log(window.innerWidth);

    }

    window.addEventListener(
        "resize",
        handleResize
    );

    return () => {

        window.removeEventListener(
            "resize",
            handleResize
        );

    };

}, []);

The cleanup function removes the event listener.

21. useEffect with Props

An effect can depend on a prop.

function Student({ studentId }) {

    useEffect(() => {

        console.log(
            "Student ID:",
            studentId
        );

    }, [studentId]);

    return (
        <h2>
            Student
        </h2>
    );

}

When studentId changes, the effect runs after the render.

22. useEffect for Synchronization

The main idea behind effects is synchronization with systems outside the normal React rendering calculation.

Examples include:

  • Browser APIs
  • Network requests
  • Timers
  • Event subscriptions
  • Third-party libraries

23. Avoid Unnecessary Effects

Not every calculation needs an effect. If a value can be calculated directly from props or state during rendering, an effect may not be necessary.

For example:

const fullName =
    firstName + " " + lastName;

This simple calculation can normally be performed directly instead of using useEffect.

24. useEffect and Conditional Logic

An effect can contain logic that should happen when a dependency changes.

useEffect(() => {

    if (isLoggedIn) {

        console.log("User is logged in");

    }

}, [isLoggedIn]);

25. Multiple useEffect Hooks

A component can have multiple effects when they represent separate synchronization tasks.

useEffect(() => {

    document.title = "React App";

}, []);

useEffect(() => {

    console.log("Count changed");

}, [count]);

Keeping unrelated effects separate can make the component easier to understand.

26. Common useEffect Mistakes

  • Forgetting to import useEffect.
  • Using an incorrect dependency array.
  • Creating timers without cleanup.
  • Adding event listeners without removing them.
  • Using effects for simple calculations that do not require synchronization.
  • Creating unnecessary effects.
  • Ignoring dependencies used by an effect.

27. Complete Timer Example

import {
    useState,
    useEffect
} from "react";

function App() {

    const [seconds, setSeconds] = useState(0);

    useEffect(() => {

        const timer = setInterval(() => {

            setSeconds(
                previousSeconds =>
                    previousSeconds + 1
            );

        }, 1000);

        return () => {

            clearInterval(timer);

        };

    }, []);

    return (
        <div>

            <h1>
                Seconds: {seconds}
            </h1>

        </div>
    );

}

export default App;

28. Complete Data Fetch Example

import {
    useState,
    useEffect
} from "react";

function App() {

    const [users, setUsers] = useState([]);
    const [loading, setLoading] =
        useState(true);

    useEffect(() => {

        fetch("https://example.com/api/users")
            .then(response => response.json())
            .then(data => {

                setUsers(data);
                setLoading(false);

            });

    }, []);

    if (loading) {

        return (
            <h2>
                Loading...
            </h2>
        );

    }

    return (
        <div>

            {users.map(user => (
                <p key={user.id}>
                    {user.name}
                </p>
            ))}

        </div>
    );

}

export default App;

29. useEffect Workflow

A simple way to understand an effect is:

  1. React renders the component.
  2. The browser updates the interface.
  3. The effect runs when its dependencies require it.
  4. The effect performs its synchronization work.
  5. If necessary, React later runs the cleanup before the effect is re-run or the component is removed.
Render
   ↓
UI Update
   ↓
Effect
   ↓
External Synchronization
   ↓
Cleanup when needed

30. useEffect Best Practices

  • Use effects for side effects and synchronization.
  • Import useEffect from React.
  • Use the dependency array carefully.
  • Include values that the effect depends on.
  • Use cleanup for timers and subscriptions when needed.
  • Remove event listeners during cleanup.
  • Avoid unnecessary effects.
  • Use state for data that needs to trigger rendering.
  • Keep unrelated effects separate when appropriate.
  • Do not use an effect for calculations that can be performed directly during rendering.

📌 Key Points

  • useEffect is used for side effects and synchronization.
  • An effect runs after a render when its dependencies require it.
  • The dependency array controls when an effect is re-synchronized.
  • An empty dependency array is commonly used for effects that do not depend on changing values.
  • Effects can be used for data fetching, timers, and browser APIs.
  • An effect can return a cleanup function.
  • Cleanup is important for timers, subscriptions, and event listeners.
  • Multiple effects can be used for separate synchronization tasks.
  • Not every calculation needs an effect.
  • Use effects carefully to avoid unnecessary work.

🧠 Quick Quiz

Question: What is the main purpose of the useEffect Hook in React?