How to Use the React useEffect Hook

  • 23 Sep, 2023
  • read

Effects on the Functional Component Lifecycle: How to Use the useEffect Hook

Introduction

React’s useEffect hook is a powerful tool that allows you to perform side effects in functional components. In this article, we will delve into the various use cases of the useEffect hook and discuss best practices. Whether you are a junior frontend engineer or an experienced developer, understanding this hook is essential for writing efficient and maintainable React code.

Understanding React’s useEffect Hook

The useEffect hook is a vital part of React’s core library and is similar to the lifecycle methods in class-based components. It provides a way to perform side effects, such as data fetching, subscriptions, or DOM manipulations, in functional components.

Basic Usage

The basic syntax for useEffect is as follows:

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Code to run on mount and every re-render
    // Return a cleanup function if needed
  });

  return (
    // JSX components
  );
}

Use Cases

  1. Fetching data

One of the most common use cases for useEffect is fetching data from an API. You can use useEffect to make asynchronous requests and update the component’s state when the data is received.

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

function MyComponent() {
  const [data, setData] = useState([]);

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

  return (
    // Render components using the fetched data
  );
}
  1. Subscriptions and event listeners

useEffect can also be used to set up and clean up subscriptions or event listeners. You can subscribe to a specific value and handle any updates within the useEffect callback. Remember to remove the event listeners when the component unmounts.

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    const subscription = subscribe(someValue, () => {
      // Handle subscription updates
    });

    window.addEventListener('resize', handleResize);

    return () => {
      unsubscribe(subscription);
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return (
    // JSX components
  );
}
  1. Manually changing the DOM

In certain scenarios, you may need to manually interact with the DOM. useEffect allows you to perform these operations without breaking the React component model.

import React, { useEffect } from "react";

function MyComponent() {
  useEffect(() => {
    const element = document.getElementById("myElement");
    element.classList.add("highlight");

    return () => {
      element.classList.remove("highlight");
    };
  }, []);

  return <div id="myElement">{/* JSX components */}</div>;
}
  1. Debouncing user input

As we mentioned earlier, useEffect hooks are great for triggering API calls on initial render. Sometimes, however, we need to make API calls based on user input. The useEffect can help us with that, but on its own, it will send an API call ever time the user types or removes a character, which can really add stress to our server usage.

Because of this, we can add a debounce function to our API call and have React call that whenever the useEffect hook is triggered.

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

function SearchBar() {
  const [searchValue, setSearchValue] = useState("");
  const [searchResults, setSearchResults] = useState([]);

  useEffect(() => {
    const delay = setTimeout(() => {
      // Perform search API call with searchValue
      // Update searchResults state
    }, 500);

    return () => clearTimeout(delay);
  }, [searchValue]);

  return (
    <div>
      <input
        type="text"
        value={searchValue}
        onChange={(e) => setSearchValue(e.target.value)}
      />
      {/* Display searchResults */}
    </div>
  );
}
  1. Geolocation Tracking

Tracking Geolocation is another common frontend task. We need a way to track the users location and update it when it changes.

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

function LocationTracker() {
  const [location, setLocation] = useState(null);

  useEffect(() => {
    if (navigator.geolocation) {
      const geoLocationWatcher = navigator.geolocation.watchPosition(
        (position) => {
          // Update location state with latitude and longitude
        },
        (error) => {
          // Handle error
        }
      );

      return () => navigator.geolocation.clearWatch(geoLocationWatcher);
    }
  }, []);

  return <div>{/* Display location */}</div>;
}

This example demonstrates how to use the useEffect hook to track the user’s geolocation. On component mount, the hook checks for geolocation support and starts a watcher to continuously update the location state as the user’s position changes. The watcher is cleaned up before unmounting the component.

  1. WebSocket Subscriptions

Similar to other publisher / subscription relationships, we can also setup socket relationships. A great example is real-time messaging.

import React, { useState, useEffect } from "react";
import WebSocketClient from "some-websocket-library";

function RealTimeServerData() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const socket = new WebSocketClient("wss://api.example.com");

    socket.onOpen(() => {
      // Subscribe to real-time updates
    });

    socket.onMessage((message) => {
      // Update data state with received message
    });

    return () => {
      socket.disconnect();
    };
  }, []);

  return <div>{/* Display real-time data */}</div>;
}

In this example, the useEffect hook is used to establish a WebSocket connection and subscribe to real-time server data. The hook initializes the WebSocket client and sets up event handlers for open and message events. When the component unmounts, the socket connection is disconnected.

When NOT to use a useEffect Hook

The useEffect hook in React allows you to perform side effects, such as fetching data, subscribing to events, or updating the DOM, inside functional components. While useEffect is a powerful tool, there are cases when you may not need to use it:

  1. No side effects are needed: If your component doesn’t require any side effects, such as fetching data, subscribing to events, or updating the DOM, then you don’t need to use useEffect. You can simply define and render the component without any additional hooks.
  2. Static data: When your component is rendering static data that doesn’t change over time, there is no need to use useEffect. Use useEffect when you need to handle dynamic data that may change during the component’s lifecycle.
  3. Purely UI-related changes: If you only need to update the UI based on changes in props or state, you can use the other hooks like useState or useReducer directly to manage the state and handle the updates in the component’s render function.
  4. Synchronous side effects: If your side effect is synchronous and doesn’t require any cleanup or dependencies, you can perform the side effect directly within the component’s body without using useEffect.

It’s important to consider the purpose of useEffect and the specific requirements of your component. If you don’t need to perform any side effects or handle dynamic data, there is no need to use the useEffect hook.

Best Practices

Here are some best practices to keep in mind when using the useEffect hook:

Dependency array: The second argument to useEffect is an optional dependency array. By providing dependencies, you can control when the effect runs. An empty dependency array will cause the effect to run only once on mount, while specific dependencies will trigger the effect whenever any of them change.

Cleanup: When your effect requires cleanup, such as unsubscribing or removing event listeners, return a cleanup function within the useEffect callback. This function will be called when the component unmounts or when the effect is re-run due to dependency changes.

Avoid unnecessary side effects: It’s essential to keep your useEffect focused on its intended purpose and avoid introducing unintended side effects. Use useEffect wisely and conscientiously to ensure clean and efficient code.

Conclusion

React’s useEffect hook is a powerful tool for handling side effects in functional components. By understanding its use cases and following best practices, you can create clean, efficient, and maintainable code. Whether you are a junior frontend engineer or a seasoned developer, mastering the useEffect hook is crucial for successful React development.

For more information and in-depth explanations, refer to the official React documentation.

Disclaimer: This article presents original content and is intended for educational purposes. External sources, when utilized, are properly cited to support the information provided.