Ad Space — Top Banner

Minified React error #301

React JavaScript Framework

Severity: Critical

What Does This Error Mean?

Minified React error #301 means React tried to update a component that is no longer mounted in the page — a component that has already been removed from the DOM. This usually happens when asynchronous code (like a fetch request or a timer) tries to update state after the component that started it has been unmounted. The full message in development mode is: 'Can't perform a React state update on an unmounted component.'

Affected Models

  • React 16 to React 17
  • React class and function components

Common Causes

  • A setTimeout or setInterval callback calls setState after the component has unmounted
  • A fetch() or async API call finishes and tries to update state, but the user has already navigated away
  • An event listener added in useEffect (or componentDidMount) was never removed when the component unmounted
  • A Promise's .then() callback updates state, but the component was removed before the Promise resolved
  • A Redux subscription or context listener updates component state after unmount

How to Fix It

  1. In useEffect, return a cleanup function that cancels any pending async work. This cleanup function runs automatically when the component unmounts.

    Example: useEffect(() => { const timer = setTimeout(() => setState(x), 1000); return () => clearTimeout(timer); }, []);

  2. For fetch requests, use an AbortController. Create one when the effect starts, pass its signal to fetch, and abort it in the cleanup function.

    Example: const controller = new AbortController(); fetch(url, { signal: controller.signal }).then(...); return () => controller.abort();

  3. Use a mounted flag pattern as a simple workaround. Set a variable to true when the component mounts, false when it unmounts, and check it before calling setState.

    Example: let isMounted = true; fetchData().then(data => { if (isMounted) setState(data); }); return () => { isMounted = false; };

  4. Remove any event listeners you add inside useEffect. Every addEventListener in a useEffect should have a matching removeEventListener in the cleanup function.

    Example: window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler);

  5. In React 18 and later, this specific warning was removed because the React team decided it caused more confusion than it prevented. If you are on React 17 or earlier, upgrading to React 18 will eliminate the warning — but the underlying memory leak may still exist.

    Even in React 18, it is good practice to cancel async operations on unmount. The warning removal does not mean the code is now correct — it just means React no longer shouts about it.

When to Call a Professional

Minified error #301 is always fixable in your own code. The fix is to cancel or ignore async operations when the component unmounts. This is a warning turned error — fixing it also prevents potential memory leaks.

Frequently Asked Questions

Why does this error only appear sometimes?

Because it is a race condition — it only triggers when the async operation finishes after the component unmounts. If the user stays on the page long enough for the request to complete, no error. If they navigate away quickly, the error fires. This timing-dependent nature makes it easy to miss in testing but very visible in production with real user behaviour.

Does this error actually break anything, or is it just a warning?

In React 16 and 17, it appears as a console warning (not a visible error to users). The underlying issue — updating unmounted components — does nothing harmful most of the time, but it can cause memory leaks if many async operations pile up. In rare cases with complex state, it can cause subtle bugs where stale state bleeds between component instances.

What is the cleanest pattern for async data fetching in React?

The cleanest approach is using a data-fetching library like React Query or SWR. These libraries handle cancellation, caching, and mounting checks automatically. You write fetchData() and the library handles the rest — no manual cleanup functions needed. For simpler cases, the AbortController pattern inside useEffect is the standard approach.