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.

Answer from ravibagul91 on Stack Overflow
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.

🌐
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

Discussions

reactjs - Returning a function in useEffect - Stack Overflow
I'm trying to understand why returning a function inside useEffect hook executes during the second time. For example: const App = (props) => { const [change, setChange] = useState(false); useEffect(() => { return () => console.log("CHANGED!"); }, [change]); return ( setChange(true)}>CLICK ); }; The first time the component renders, 'CHANGED' is not being logged, but after I click the button, it does. Does anyone know why? ... Per the React ... More on stackoverflow.com
🌐 stackoverflow.com
reactjs - UseEffect return value - Stack Overflow
Thanks for the reply Kevin. Would you be able to explain what I should be doing. I’m a total beginner with react. What function doesn’t return a value? The useEffect? More on stackoverflow.com
🌐 stackoverflow.com
reactjs - React useEffect and return render - Stack Overflow
I have a useEffect with fetch and I would like to render it first, since it runs the else case first and then runs the useEffect. Which shows the else case then the posts when viewing the page. Is ... More on stackoverflow.com
🌐 stackoverflow.com
When does the useEffect's callback's return statement execute?
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 ... More on stackoverflow.com
🌐 stackoverflow.com
People also ask

How can I prevent unnecessary re-renders and re-executions of useEffect in my React components?
You can avoid unnecessary re-renders by properly using the dependency array in useEffect. Only include variables that truly impact the side effect. Also, memoize callbacks using useCallback and memoize values with useMemo where applicable. Avoid creating new object or function references in your component unless they’re needed, as those trigger a new render or effect. And remember, React.memo can help you memoize whole components, preventing them from re-rendering unless props change.
🌐
knowledgehut.com
knowledgehut.com › home › blog › software development › how to use useeffect in react js: a beginner-friendly guide
React useEffect() Hook: Structure, Usage, Examples & Tips
How can I optimize performance in large React apps that use multiple useEffect hooks?
Start by ensuring each useEffect hook is scoped to a specific task—don't overload one hook with multiple concerns. Use dependency arrays wisely to avoid over-triggering. Where possible, batch state updates or debounce expensive operations like API calls or calculations. Also, consider lazy-loading components, code splitting, and using React’s built-in Suspense and React.memo for optimization.
🌐
knowledgehut.com
knowledgehut.com › home › blog › software development › how to use useeffect in react js: a beginner-friendly guide
React useEffect() Hook: Structure, Usage, Examples & Tips
Why do my useEffect dependencies cause infinite loops even when the values don’t seem to change?
This typically happens when you include non-primitive values like objects, arrays, or functions directly in the dependency array. React compares dependencies using reference equality, so even if the content is the same, a new reference will be treated as a change. To fix this, you can memoize those dependencies using useMemo or useCallback, or restructure your logic to avoid including them if unnecessary.
🌐
knowledgehut.com
knowledgehut.com › home › blog › software development › how to use useeffect in react js: a beginner-friendly guide
React useEffect() Hook: Structure, Usage, Examples & Tips
🌐
React
react.dev › reference › react › useEffect
useEffect – React
import { useState, useEffect } from 'react'; import { fetchBio } from './api.js'; export default function Page() { const [person, setPerson] = useState('Alice'); const [bio, setBio] = useState(null); useEffect(() => { let ignore = false; setBio(null); fetchBio(person).then(result => { if (!ignore) { setBio(result); } }); return () => { ignore = true; } }, [person]); return ( <> <select value={person} onChange={e => { setPerson(e.target.value); }}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> <option value="Taylor">Taylor</option> </select> <hr /> <p><i>{bio ??
🌐
React
legacy.reactjs.org › docs › hooks-effect.html
Using the Effect Hook – React
We declare the count state variable, and then we tell React we need to use an effect. We pass a function to the useEffect Hook. This function we pass is our effect. Inside our effect, we set the document title using the document.title browser API. We can read the latest count inside the effect because it’s in the scope of our function.
🌐
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. Clean up the timer at the end of the useEffect Hook: import { useState, useEffect } from 'react'; import { createRoot } from 'react-dom/client'; function Timer() { const ...
🌐
Design+Code
designcode.io › react-hooks-handbook-useeffect-hook
useEffect Hook - React Hooks Handbook - Design+Code
An overview of React Hooks and the frameworks you can use to build your React application blazingly fast ... Learn the basics of asynchronous functions and promises by fetching data from an API using fetch, useEffect and useState
Find elsewhere
🌐
GeeksforGeeks
geeksforgeeks.org › reactjs › reactjs-useeffect-hook
React useEffect Hook %%page%% %%sep%% %%sitename%% - GeeksforGeeks
Initial Render Happens: React renders the component and updates the DOM. useEffect Executes After Render: It runs after the paint, not during render. Dependencies Are Checked: Runs after every render without a dependency array, once on mount with [], and only on dependency change when values are provided. Cleanup Function Runs: Before the effect re-runs or the component unmounts, the cleanup function (returned from useEffect) is executed.
Published   1 week ago
🌐
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 …
🌐
KnowledgeHut
knowledgehut.com › home › blog › software development › how to use useeffect in react js: a beginner-friendly guide
React useEffect() Hook: Structure, Usage, Examples & Tips
July 22, 2025 - React’s useEffect can replicate all these lifecycle methods depending on how you configure the dependency array: For componentDidMount: use an empty dependency array ([]). For componentDidUpdate: include specific dependencies in the array. For componentWillUnmount: return a cleanup function inside useEffect.
🌐
DEV Community
dev.to › cassidoo › when-useeffect-runs-3pf3
When useEffect runs - DEV Community
June 4, 2024 - Changing that z variable in here does not change the return value of the function, it's just a side effect of adding x and y! Now in React/Preact, it's a bit more complex, and side effects aren't always a good thing. And useEffect is "usually" for side effects.
🌐
LogRocket
blog.logrocket.com › home › understanding react’s useeffect cleanup function
Understanding React’s useEffect cleanup function - LogRocket Blog
December 16, 2024 - According to React’s official documentation, “The cleanup function runs not only during unmount, but before every re-render with changed dependencies. Additionally, in development, React runs setup+cleanup one extra time immediately after component mounts.” · As a side note before we continue: useEffects can be made to run once by simply passing an empty array to the dependency list.
🌐
React
react.dev › reference › react › useEffectEvent
useEffectEvent – React
When you call the returned Effect Event function, the callback always accesses the latest committed values from render at the time of the call. useEffectEvent returns an Effect Event function with the same type signature as your callback.
🌐
Upgrad
upgrad.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 - React’s useEffect can replicate all these lifecycle methods depending on how you configure the dependency array: For componentDidMount: use an empty dependency array ([]). For componentDidUpdate: include specific dependencies in the array. For componentWillUnmount: return a cleanup function inside useEffect.
🌐
Reacttraining
reacttraining.com › blog › useEffect-cleanup
The useEffect cleanup and the two circumstances it's called.
March 21, 2023 - The cleanup function is a function returned from within the effect function.
Top answer
1 of 3
4

You can get that result, but you'll need to do it a different way.

When your component renders for the first time, the entire function will run, and then the page will be updated based on what your function returned. This happens more or less synchronously. Only after it has rendered will your effects run. It's not possible to change the order of this: render is always first, effects are always after the render.

So if you don't want anything to display until that effect can complete, you need to return a null from the render. Then when the fetch is complete, you update state, which causes it to render again, this time not with a null.

For example, here i've taken your code and added one more possibility: the state might be null. I'm using this to indicate that it hasn't loaded yet. Later, once the data has been fetched, the state gets updated and the component rerenders.

const [posts, setPosts] = useState(null);

useEffect(() => {
  (async () => {
    const data = await fetch('https://localhost:1111/api/posts', {
      credentials: 'include'
    });
    const response = await data.json();
    setPosts(response);
  })();
}, []);

if (posts === null) {
  return null;
}

return (
  <div>
    {posts.length > 0 ? (
      posts.map(post => (
        <Post key={post.id} onUpdate={() => handleUpdate()} post={post} />
      ))
    ) : (
      <p>No posts</p>
    )}
  </div>
);
2 of 3
0

You could set posts to null by default, rather than an empty array. Then render nothing (or a spinner) if posts is null. This would prevent flashing "no posts" while the data fetching is occurring.

🌐
Dmitri Pavlutin
dmitripavlutin.com › react-useeffect-explanation
A Simple Explanation of React.useEffect() - Dmitri Pavlutin
January 28, 2023 - Let's return a cleanup function that stops the previous timer before starting a new one: ... Open the demo. Open the demo and type some messages. You'll see that every 2 seconds only the latest message logs to the console. Which means that all of the previous timers were cleaned up. useEffect() can perform data fetching side-effect.
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?

🌐
Isotropic
isotropic.co › how-to-fix-the-useeffect-must-not-return-anything-besides-a-function-warning
How To Fix The "useEffect must not return anything besides a function" Warning - Isotropic
August 22, 2022 - That is because the callback itself is returning a promise which is incompatible with useEffect. The fix here is actually quite simple. You should wrap your asynchronous work in a function and then just call that from the useEffect callback.