How to Secure useEffect Hook in React

This article explains how to secure the useEffect hook in React to prevent security vulnerabilities, memory leaks, and performance degradation. You will learn essential best practices for securing your hook, including implementing cleanup functions, handling asynchronous race conditions, managing dependency arrays, and protecting against data injection.

1. Prevent Memory Leaks with Cleanup Functions

When useEffect performs side effects like setting up subscriptions, event listeners, or intervals, failing to clean them up can cause memory leaks. Memory leaks can degrade application performance and leave open connections vulnerable to exploitation.

Always return a cleanup function at the end of your useEffect block:

useEffect(() => {
  const handleResize = () => {
    console.log(window.innerWidth);
  };

  window.addEventListener('resize', handleResize);

  // Cleanup function
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

2. Eliminate Race Conditions in Data Fetching

When fetching data inside useEffect, network requests might complete in a different order than they were initiated. If a user quickly switches between pages, a slower, outdated request might overwrite the newest data. This is known as a race condition and can lead to displaying incorrect data.

To secure your data fetching, use a boolean flag or an AbortController to ignore outdated responses:

useEffect(() => {
  let isActive = true;

  const fetchData = async () => {
    const response = await fetch(`https://api.example.com/data/${id}`);
    const data = await response.json();
    
    if (isActive) {
      setData(data);
    }
  };

  fetchData();

  return () => {
    isActive = false; // Prevents updating state on unmounted components
  };
}, [id]);

3. Manage the Dependency Array Correctly

An incorrectly configured dependency array can cause infinite render loops, which can crash the browser or overwhelm your backend APIs with requests—effectively creating a self-inflicted Denial of Service (DoS) attack.

4. Sanitize Fetched Data to Avoid XSS

When useEffect fetches data from an API, treating that data as inherently safe is a security risk. If the API response contains malicious scripts and you render it using unsafe methods (such as dangerouslySetInnerHTML), you expose your application to Cross-Site Scripting (XSS) attacks.

Always sanitize third-party data before rendering it:

import DOMPurify from 'dompurify';

useEffect(() => {
  fetch('https://api.example.com/user-content')
    .then(res => res.json())
    .then(data => {
      // Sanitize the HTML string before saving it to state
      const cleanHtml = DOMPurify.sanitize(data.htmlContent);
      setContent(cleanHtml);
    });
}, []);