The return in useEffect is a cleanup function for any event listeners and such that should be turned off after the effect runs to prevent leaks. If you're not setting event listeners, canceling asynchronous requests or you don't have any resources to clean up then you don't need anything in the return. You should put your code/function calls before the return function. With an empty dependency array at the end of your useEffect means it will run one time when the component is rendered. If this isn't the behavior you want then you'll need to specify dependencies in the array to only run the effect if the dependencies change. Answer from WranglerReasonable91 on reddit.com
🌐
Reddit
reddit.com › r/react › how does the return function in useeffect work?
r/react on Reddit: How does the return function in UseEffect work?
November 10, 2023 -

Hi,

I've a really simple component whose only purpose is to render an error message that I manage with redux. My thought was to return a function in the useEffect that clears the error message (something like dispatch(clearMessage)) because, to my knowledge, that function should run when the component is unmounted, which should occur only when navigating to another component.

I want to clear the message when unmounting in order to avoid the user going to the /error route by, for example, a redirect if there's no message.

So, I have another component that fetches data using react-query and, in case of error, use the dispatch to set the error message and then redirects the user to the error component.

The thing is, when I'm redirected to the error component, I see unmounting in the console immediately (which also runs clearMessage when I try to implement my idea, so it doesn't work as I expected). I don't get why, I guess there's some concept I'm missing.

So, what am I doing wrong?

import { useSelector } from 'react-redux'
import { selectError } from '../feature/error/errorSlice'
import { useEffect } from 'react'
const Error = () => {
const message = useSelector(selectError)
useEffect(() => {
return () => {
console.log('unmounting')
}
}, [])
return (
<h2>
{message}
</h2>
)
}
export default Error

Top answer
1 of 8
88

1. Why is the return a function? return () => { ignore = true };

From the docs:

Why did we return a function from our effect? This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They’re part of the same effect!

and

When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time. We’ll discuss why this helps avoid bugs and how to opt out of this behaviour in case it creates performance issues later below.

2. What is ignored used for in this example?

Initially in useEffect Hook ignore is set like, let ignore = false;.

When fetchProduct function executes it checks for ignore is true and accordingly sets setProduct(json).

This means we have state called product and setting the value in state using setProduct(json).

This product in state is used to render details on page.

Note: As [productId] is passed as second argument to useEffect, fetchProduct function will only get executes when productId changes.

See optimizing performance by skipping effects.

2 of 8
83

I will explain it here, as it took me a while to understand the explanations above, so I will try to make it simpler for others. Answers to your questions:

Why is return a function?

return () => { ignore = true };

useEffect offers the return function which is used for cleanup purposes. OK!, When do you need a cleanup? If You subscribe to something (like an event listener) and want to unsubscribe from it, this is where You should add the unsubscription logic to prevent a race condition or other issues!

What is ignore used for in this example?

ignore is used as a flag that tells your effect to ignore, or not, the API call. When is that used? When a race condition might occur.

Example of a race condition:

Imagine You have a list of products, and You fetch product details when a product is clicked. If You click on products quickly, multiple API calls might be in progress simultaneously, resulting in changes to product_id that would trigger useEffect to run multiple times. Here's what could happen:

  1. Click on product1: an API call for product1 will trigger.
  2. Quickly click on product2: triggers API call for product2.
  3. API call for product2 finishes first and the page updates with product2 data
  4. API call for product1 finishes later, and the page updates with product1 data.

Now data for product1 is displayed on the page, even though product2 was clicked last. This is a race condition.

BUT WHAT DOES THAT HAVE TO DO WITH ignore??

Logic explanation

ignore, my dear, tells setProduct to ignore the data from product1, and to not update the state with It, or in other words, It is used to ensure outdated API calls don't update the state when a race condition occurs.

Code explanation

First scenario : No race condition

  1. Click on product1:
    • ignore = false
    • call API for product1
    • Data arrives
    • If (!ignore) = true -> update state with product1 data
    • Set ignore = true inside cleanup function.
    • Congrats! product1 data is displayed correctly.

Second scenario: Race condition

  1. product1 is clicked
    • ignore = false
    • call API for product1
    • data still hasn't arrived
  2. Quickly click on product2
    • ignore = false
    • call API for product2
  3. Data for product2 arrives first
    • !ignore is true -> update state with product2 data
    • Cleanup function sets ignore = true
  4. Data for product1 arrives later
    • if (!ignore) = false (because ignore = true)
    • Oops, old data is ignored, so product1 data does not update the state.

And this is how we will always have up-to-date data.

Discussions

Return statement in useEffect - javascript
I'm trying to fetch data through some API and map into my product page. The problem is when I'm writing the return statement inside useEffect, output is not showing in my console, but when I commen... More on stackoverflow.com
🌐 stackoverflow.com
In React, does a return statement return immediately or does it wait for the UseEffect to finish?
Either way, 'user' will always be returned first and only then the code in useEffect will run. ... In a shorter and simpler statement: useEffect runs only after the component finished rendering its JSX. More on stackoverflow.com
🌐 stackoverflow.com
reactjs - Why can't useEffect access my state variable in a return statement? - Stack Overflow
I don't understand why my useEffect() React function can't access my Component's state variable. I'm trying to create a log when a user abandons creating a listing in our app and navigates to another More on stackoverflow.com
🌐 stackoverflow.com
Returning empty object in useEffect
The purpose of the return function in useEffect is usually for code cleanup, in this example, it's doing nothing. More on reddit.com
🌐 r/reactjs
24
5
April 1, 2022
Top answer
1 of 3
29

Your understanding of the sequence of events is correct. The only thing missing is the precise timing of the effect callbacks and cleanup.

When the component re-renders, any useEffects will have their dependency arrays analyzed for changes. If there has been a change, then that effect callback will run. These callbacks are guaranteed to run in the order that they're declared in the component. For example, below, a will always be logged just before b.

const App = () => {
    const [num, setNum] = React.useState(0);
    React.useEffect(() => {
      setInterval(() => {
        setNum(num => num + 1);
      }, 1000);
    }, []);
    React.useEffect(() => {
      console.log('a', num);
    }, [num]);
    React.useEffect(() => {
      console.log('b', num);
    }, [num]);
    return num;
}

ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

These effect callbacks will run shortly after the browser re-paints.

Now add the effect cleanup callback into the mix. These will always run synchronously just before the effect callback for a render runs. For example, let's say the component starts at Render A, and in Render A, an effect hook has returned a cleanup callback. Then, some state changes, and a transition to Render B occurs, and there exists a useEffect with a dependency array that includes the state change. What will happen is:

  • The functional component will be called with the new props/state, for Render B
  • The component returns the new markup at the end of the function
  • The browser repaints the screen if necessary
  • The cleanup function from render A will run
  • The effect callback from render B will run

You can see the source code for those last two actions here:

commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork);
commitHookEffectListMount(Passive$1 | HasEffect, finishedWork);

That first call invokes all cleanup callbacks from a prior render. That second call invokes all effect callbacks for the current render. Current render effect callbacks run synchronously after the execution of prior render cleanup callbacks.

2 of 3
14

Does a functional component 'dismount' after a change in its state and does this result in previous useEffect callback return statements executing?

No, component dismounts only once at end of its life time, React allows to execute a callback with useEffect hook by providing an empty dep array with a return statement:

useEffect(() => {
  return () => {
    console.log("unmounts");
  };
}, []);

When component dismounts?

When its parent stops rendering it. See conditional rendering.


Could you help me understand, in general or in the example in my question, when the return statement of useEffect executes?

Depends on the dep array:

  • if it's empty [], on unmount.
  • if it has dependencies [value1,value2], on dependencies change (shallow comparison).
  • if it has no dependencies (no 2nd argument for useEffect) it runs on every render.

See follow up question useEffect in depth / use of useEffect?

🌐
W3Schools
w3schools.com › react › react_useeffect.asp
React useEffect Hooks
We do this by including a return function at the end of the useEffect Hook.
🌐
Design+Code
designcode.io › react-hooks-handbook-useeffect-hook
useEffect Hook - React Hooks Handbook - Design+Code
One of the tools used for managing the state is the useEffect hook, that deals with the component's lifecycle. Read on to learn how to use the useEffect hook. ... Purchase includes access to 50+ courses, 320+ premium tutorials, 300+ hours of videos, source files and certificates.
🌐
React
react.dev › reference › react › useEffect
useEffect – React
It should return a cleanup function with cleanup code that disconnects from that system. A list of dependencies including every value from your component used inside of those functions.
🌐
LogRocket
blog.logrocket.com › home › understanding react’s useeffect cleanup function
Understanding React’s useEffect cleanup function - LogRocket Blog
December 16, 2024 - The useEffect Hook is designed to allow the return of a function within it, which serves as a cleanup function. The cleanup function prevents memory leaks — a situation where your application tries to update a state memory location that no ...
Find elsewhere
🌐
Tutorial45
tutorial45.com › home › blog › return inside useeffect in reactjs: why?
Return Inside useEffect in ReactJS: Why? - Tutorial45
February 18, 2024 - When we return a function inside our effect, it will be queued for execution before the component unmounts and subsequent effects are run. This function has come to be known as the ‘cleanup function,’ and it’s this function that will ‘clean ...
🌐
Digital Primates
digitalprimates.net › home › javascript › what values should be returned…
What values should be returned when using useEffect in React? - Digital Primates
February 2, 2021 - Based on the information from the React docs we can imply that when using the useEffect hook, we can either return a destroy function or not explicitly return any value, implicitly returning undefined. But what if we return something other than a destroy function or undefined?
🌐
TutorialsPoint
tutorialspoint.com › using-useeffect-in-react-js-functional-component
Using useEffect() in React.js functional component
September 4, 2019 - Inside useEffect we can add a return statement at the end of function call which returns a function. This return function does the cleanup work.
🌐
Medium
caesar-jd-bell.medium.com › how-to-properly-clean-up-effects-in-useeffect-9e6ab497eb02
How to Properly Clean Up Effects in useEffect | by Caesar Bell | Medium
January 3, 2023 - When you return a function from useEffect, React will use that function as a clean-up function that runs before the component is unmounted or the effect is re-run. Here’s a basic example of using the return statement to clean up an effect:
🌐
React
legacy.reactjs.org › docs › hooks-effect.html
Using the Effect Hook – React
We’ll discuss why this helps avoid bugs and how to opt out of this behavior in case it creates performance issues later below. ... We don’t have to return a named function from the effect. We called it cleanup here to clarify its purpose, but you could return an arrow function or call it something different. We’ve learned that useEffect lets us express different kinds of side effects after a component renders.
🌐
KnowledgeHut
knowledgehut.com › home › blog › software development › how to use useeffect in react js: a beginner-friendly guide
useEffect in React JS Explained with Simple Examples!
July 22, 2025 - It ensures your app remains reactive by automatically responding to changes in ... It simplifies cleanup: With a return function that runs on component unmount or before the effect runs again.
Top answer
1 of 4
42

I think it is a typical stale closure problem. And it is hard to understand at first.

With the empty dependency array the useEffect will be run only once. And it will access the state from that one run. So it will have a reference from the logAbandonListing function from this moment. This function will access the state from this moment also. You can resolve the problem more than one way.

One of them is to add the state variable to your dependency.

  useEffect(() => {
    return () => logAbandonListing()
  }, [progress])

Another solution is that you set the state value to a ref. And the reference of the ref is not changing, so you will always see the freshest value.

let[progress, setProgress] = React.useState(0);
const progressRef = React.createRef();
progressRef.current = progress;

...

  const logAbandonListing = () => {
    console.log(`progress inside: ${progressRef.current}`)
    if (progressRef.current > 0) {
      addToLog(userId)
    }
  }

If userId is changing too, then you should add it to the dependency or a reference.

2 of 4
7

To do something in the state's current value in the useEffect's return function where the useEffects dependencies are am empty array [], you could use useReducer. This way you can avoid the stale closure issue and update the state from the useReducer's dispatch function.

Example would be:

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

function reducer(state, action) {
  switch (action.type) {
    case "set":
      return action.payload;
    case "unMount":
      console.log("This note has been closed: " + state); // This note has been closed: 201
      break;
    default:
      throw new Error();
  }
}

function NoteEditor({ initialNoteId }) {
  const [noteId, dispatch] = useReducer(reducer, initialNoteId);

  useEffect(function logBeforeUnMount() {
    return () => dispatch({ type: "unMount" });
  }, []);


  return <div>{noteId}</div>;
}
export default NoteEditor;

More info on this answer

🌐
Medium
medium.com › @MohamedHNoor › how-to-fix-the-useeffect-must-not-return-anything-besides-a-function-warning-in-react-98cee5b73cd5
How To Fix The “useEffect must not return anything besides a function” Warning in React Applications | by Mohamed Hassan Noor | Medium
February 10, 2023 - If you are getting the “useEffect must not return anything besides a function” warning, it means that you have returned something other than a function from your useEffect hook.
🌐
Medium
medium.com › @tessintaiwan › the-return-of-useeffect-react-981012626f4e
the “return” of useEffect(React) - TESS - Medium
January 17, 2024 - the “return” of useEffect(React) Basically, uesEffect receive 2 variable, first one is a function, the second one is a array. useEffect(()=>{ console.log('hello') }, []) The second variable …