Tag: Hooks

  • Understanding the useEffect Hook in React

    What is useEffect?

    useEffect is a React hook that lets functional components perform actions after rendering. These actions can include fetching data, updating the DOM, setting up timers, or handling events. Before hooks, class components used lifecycle methods like componentDidMount and componentWillUnmount to do this.

    With useEffect, all these behaviors can be managed inside a functional component without needing a class.


    How to use useEffect

    To use useEffect, first import it from React and then call it inside your component:

    Example: Fetching Data

    import { useEffect, useState } from "react";
    
    function FetchData() {
      const [data, setData] = useState(null);
    
      useEffect(() => {
        fetch("https://jsonplaceholder.typicode.com/posts/1")
          .then((response) => response.json())
          .then((json) => setData(json));
      }, []);
    
      return (
        <div>
          <h3>Post:</h3>
          <p>{data ? data.title : "Loading..."}</p>
        </div>
      );
    }

    What happens here?

    • useEffect runs when the component first appears on the screen.
    • The empty [] means it only runs once.
    • The effect fetches data and updates the state with setData.

    When does useEffect run?

    The second argument of useEffect is called the dependency array. It tells React when to run the effect.

    1. Updating Document Title

    import { useEffect, useState } from "react";
    
    function TitleUpdater() {
      const [title, setTitle] = useState("React App");
    
      useEffect(() => {
        document.title = title;
      }, [title]);
    
      return (
        <input 
          type="text" 
          value={title} 
          onChange={(e) => setTitle(e.target.value)} 
          placeholder="Update page title" 
        />
      );
    }
    • The document title updates every time title changes.

    2. Auto-Saving Form Input

    import { useEffect, useState } from "react";
    
    function AutoSaveForm() {
      const [input, setInput] = useState("");
    
      useEffect(() => {
        const timeout = setTimeout(() => {
          console.log("Auto-saving input:", input);
        }, 1000);
    
        return () => clearTimeout(timeout);
      }, [input]);
    
      return (
        <input 
          type="text" 
          value={input} 
          onChange={(e) => setInput(e.target.value)} 
          placeholder="Type something..." 
        />
      );
    }
    • Waits 1 second after typing to “auto-save” (simulated with console.log).

    Cleaning up Effects

    Some effects need cleanup, like event listeners and timers. We return a function inside useEffect to clean up.

    Example: Handling Window Resize

    useEffect(() => {
      const handleResize = () => {
        document.body.style.backgroundColor = window.innerWidth > 800 ? "lightblue" : "lightcoral";
      };
    
      window.addEventListener("resize", handleResize);
      handleResize();
    
      return () => {
        window.removeEventListener("resize", handleResize);
      };
    }, []);
    • Dynamically changes the background color based on window width.

    Example: Pausing Background Music

    useEffect(() => {
      const audio = new Audio("/background-music.mp3");
      audio.play();
    
      return () => {
        audio.pause();
      };
    }, []);
    • Plays background music when mounted and pauses when unmounted.

    Common Mistakes with useEffect

    1. Forgetting Dependencies

    useEffect(() => {
      fetchData();
    }, []); // fetchData is missing in dependencies!

    If fetchData depends on props or state, it should be in the dependency array.

    2. Causing an Infinite Loop

    useEffect(() => {
      setCount(count + 1); // Triggers re-render forever!
    }, [count]);

    This keeps updating count, causing an endless loop.

    3. Not Cleaning Up

    useEffect(() => {
      window.addEventListener("scroll", handleScroll);
    }, []);

    If not removed, this event listener stays in memory forever, leading to performance issues.


    useEffect vs. useLayoutEffect

    • useEffect runs after the screen updates (good for data fetching and timers).
    • useLayoutEffect runs before the screen updates (good for layout changes).

    Example:

    useLayoutEffect(() => {
      document.body.style.opacity = "1";
    });

    Most of the time, useEffect is the better choice.


    Summary

    • useEffect handles side effects in functional components.
    • Runs after every render, once on mount, or when dependencies change.
    • Cleanup prevents memory leaks and unnecessary operations.
    • Avoid missing dependencies and infinite loops.