惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
宝玉的分享
宝玉的分享
小众软件
小众软件
有赞技术团队
有赞技术团队
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
MyScale Blog
MyScale Blog
Engineering at Meta
Engineering at Meta
Stack Overflow Blog
Stack Overflow Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MongoDB | Blog
MongoDB | Blog
WordPress大学
WordPress大学
J
Java Code Geeks
罗磊的独立博客
V
Visual Studio Blog
雷峰网
雷峰网
H
Help Net Security
T
The Blog of Author Tim Ferriss
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
大猫的无限游戏
大猫的无限游戏
F
Fortinet All Blogs

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Ultimate Guide on React cleanup function
jabo Landry · 2026-05-17 · via DEV Community

If you've been using React for some time you may be familiar with the term "cleanup function" when working with side effects, it may be confusing at first or sometimes you don't make sense out of it.

In this guide I will explain the use of cleanup function so you can start using it with confidence and clarity.

Table of Contents

What is a cleanup function

A cleanup function in React is the function you return from inside a useEffect, React calls cleanup function when the component unmounts or before the effect re‑runs, so you can stop timers, cancel requests, or remove event listeners.

In development, StrictMode intentionally mounts, unmounts, and re‑mounts components once to check that your cleanup logic works correctly. This helps catch bugs with side effects early.

Not every effect needs cleanup — only those that start something ongoing (like a subscription or fetch).
Without cleanup, you risk inconsistent UI updates or memory leaks.

Why use a cleanup function (with example)

Let's take an example of fetching data from an API and have the pagination in place and a select menu to select where you to start fetching data.

First create a function to fetch the comments using a dummy API (https://dummyjson.com/comments) that receives an argument of where to start while fetching the data.

export default function CleanUpComponent(start) {
  function fetchComments(start) {
    return new Promise((resolve) => {
      setTimeout(() => {
        fetch(`https://dummyjson.com/comments?skip=${start}`)
          .then((res) => {
            resolve(res.json());
          })
          .catch((err) => Error(err));
      }, 3000);
    });
  }
}

Enter fullscreen mode Exit fullscreen mode

In the function you return a new promise and then we add setTimeout function to wait for three (3) second before returning the response,

Note: "that the setTimeout here is just to simulate a slow network for demonstration purposes — in a real app you wouldn't need it".

why setTimeout? to observe the importance of having the cleanup function we need to delay the time it may take to respond for the users with poor network signals or in any case where we the network may be slow for some reason.

And then create a state that will track the selected starting id when fetching and then the state to store and display the fetched data.

import { useEffect, useState } from "react";

export default function CleanUpComponent() {

  const [dataDisplay, setDisplay] = useState([]);
  const [loading, setLoading] = useState(true);
  const [start, setStart] = useState(0);

  function fetchComments(start) {
    return new Promise((resolve) => {
      setTimeout(() => {
        fetch(`https://dummyjson.com/comments?skip=${start}`)
          .then((res) => {
            resolve(res.json());
          })
          .catch((err) => Error(err));
      }, 3000);
    });
  }

  useEffect(() => {
    setLoading(true);

    fetchComments(start)
      .then((res) => setDisplay(res["comments"]))
      .catch((err) => console.error(err))
      .finally(() => setLoading(false));
  }, [start]);
}

Enter fullscreen mode Exit fullscreen mode

Inside the component then add a useEffect to perform the fetch operation and synchronize the application with you’re react application, and we can display the data on the screen.

  return (
    <>
      <select
        name=""
        id=""
        onChange={(e) => setStart(Number(e.currentTarget.value))}
      >
        <option value="select an option">--select an option to start with</option>
        <option value="5">5</option>
        <option value="10">10</option>
        <option value="15">15</option>
      </select>

      {loading && <p>loading....</p>}

      {!loading &&
        dataDisplay.slice(0, 6).map((element) => {
          return (
            <p key={element.id}>
              {element.id}: {element.body}
            </p>
          );
        })}
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

This approach works but there is an issue in the above snippet, if you try choosing a different number in the option menu to start with fetching while the previous fetching is still going on.

The UI will show inconsistent data which can cause frustration to user using the site.

If you observe the above demo example you can see that we first select 10 as the starting point while it is still fetching,

we change the number to start at 5 and after a few seconds in our case 3 seconds remember we’ve used it as a delay for the timer to call a function,

the screen shows the data from 11 but then suddenly that UI changes to data from 6.

This kind of data inconsistency is known as a race condition. A race condition occurs when multiple async requests finish out of order, and an outdated request updates the UI after a newer one.

if you were reading through fetched data and suddenly the UI changes the data that a big frustration and a red flag to a site.

Cause

The cause for this kind of behavior is that when fetching the useEffect will send the first request while it is still going on we fire another fetch and then the first request will resolve after a few seconds the last request will also resolve and updates the existing state and component re-renders which updates the UI.

Solution:

The solution is to use a cleanup function in the useEffect to clear out any previous running task and instead continue with the current requested request, the cleanup function is defined by returning function that will clear out the previous request.

For this example, use the abortController to cancel any previous running task in the background, change the function to fetch to receive the second argument which will be a controller which will use to cancel the previous request.

if you are not familiar with abortController you can read more about it using this AbortController - Web APIs | MDN

 function fetchComments(start, controller) {
    return new Promise((resolve) => {
      setTimeout(() => {
        fetch(`https://dummyjson.com/comments?skip=${start}`, {
          signal: controller.signal,
        })
          .then((res) => {
            resolve(res.json());
          })
          .catch((err) => Error(err));
      }, 3000);
    });
  }

Enter fullscreen mode Exit fullscreen mode

There is the signal option on fetch API that helps to abort or cancel request based on the signal provided, and abortController we have a signal property that ties together the current fetch call signal with the abortController and then abort the previous request in the cleanup function.

useEffect(() => {
     //creation of abortcontroller.
    const controller = new AbortController();

    setLoading(true);
    fetchComments(start, controller)
      .then((res) => setDisplay(res["comments"]))
      .catch((err) => console.error(err))
      .finally(() => setLoading(false));

   // cleanup function to abort the previous request.
    return () => {
      controller.abort();
    };
  }, [start]);

Enter fullscreen mode Exit fullscreen mode

Create a controller variable to hold the abortController instance and the pass it in the function you have created to fetch the data and in the cleanup function call the abort() on the controller instance to stop the request that was not finished.

Now if we try switching the options instantly the data will be consistence and they will be no race conditions.

I have added logs before the cleanup and after to show you how the cleanup works, if you want you can add a console log before the abort and another one after aborting the request.

now if you were to switch to another number while there are some fetching that are going on they will be aborted and then fetch the latest request now you can see that there are no flash data changes like we had before.

Summary

The cleanup function is essentially used to remove and clear side effects tasks that are still running in the background when the component is unmounted, or the effect is going to re-run.

To avoid problems like race conditions, memory leaks and many more issues that can be caused the side effects fetching.