-
Notifications
You must be signed in to change notification settings - Fork 47.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
useEffect memory leak when setting state in fetch promise #15006
Comments
If you're getting that warning, it means that your Also it looks like you're under-specifying dependencies for your |
When loading the component I added a |
Can you try and create a CodeSandbox that reproduces the problem? It's difficult to try and setup and run a full application that makes actual API calls and requires user tokens. |
To avoid calling setState after unmount from a hook, you can use the useEffect callback to set a ref value on unmount, and use that as a guard in your promise callbacks to check whether the component is still mounted before updating state. Here's an example: https://github.com/ghengeveld/react-async/blob/master/src/useAsync.js But to be honest you're probably better off not reinventing this wheel and use useAsync directly, because it's fully tested and covers other edge cases as well. |
@ghengeveld Installing useAsync resolved the issue I was having |
Let's keep it open though. There might be a false positive warning in some cases. It would really help if you turned this into a CodeSandbox... |
Alright I will try my best to recreate it maybe using another API like a weather API. The only reason I couldn't here was because I was using Spotify and you need to have an account to get an access token |
I tried to recreate the warning but I'm not getting it to show in this demo |
Hi, I was getting this warning as well.
Since we can't cancel out a Promise here's an article that explains a workaround.
Edit: in your example try putting a console.log before the promise resolving (or rejecting) and see if it's being called. In my project I also got random warnings like those until I "guarded" all of the state updates on promises. Never saw one again. Edit2: Took a look at the codebase, you're not using the signal on the fetch calls so the ac.abort call is not aborting them. Like so |
same question
cancel promise solution const { adcode, regions = [] } = regionData;
const [regionName, setRegionName] = useState('all');
const [tableData, setTableData] = useState(regions);
useEffect(() => {
let isSubscribed = true;
// getRegionName(adcode).then(setRegionName);
getRegionName(adcode).then(() => {
if(isSubscribed) {
return setRegionName;
}}
);
const promises = [];
regions.forEach(item => {
promises.push(
getRegionName(item.code)
.then(name => {item.name = name;})
// eslint-disable-next-line no-console
.catch(() => console.log('error', item.code)),
);
});
Promise
.all(promises)
.then(() => {
if(isSubscribed) {
return setTableData(regions.filter(({ name }) => !!name));
}
});
return () => isSubscribed = false;
}, [adcode, regionData, regions]); |
How to clean up subscriptions in react components using AbortController |
Facing same issue
|
I have the similar issue, I have canceled the axios request, but I get this Warning only on the first unmount of a component. This warning does not happen on the next umount of a component. See below codes: import React, { useEffect } from "react"; export default function ContentOverall() {
} const useDataApi = (initialUrl) => {
}; export { useDataApi }; |
I have a similar problem I open a websocket connection and store it in a useState, I pass that connection into props for 4 routes. Every time I launch an "emit" on these pages, I get an error like that for each route I visited before This is the code of my page import React, { useState, useEffect } from "react";
import { useCookies } from "react-cookie";
import api from "../../services/api";
import apiHeader from "../../utils/apiHeader";
import ModalCaller from "../../components/modalCaller";
const WaitingPage = ({ websocket }) => {
const [menuOpen, setMenuOpen] = useState(false);
const [passwords, setPasswords] = useState([]);
const [pass, setPass] = useState(false);
const [cookies] = useCookies(["token"]);
const handleToggleModalCaller = (toggle, currentPass) => {
websocket.emit("call", { pass: currentPass });
setMenuOpen(toggle);
setPass(currentPass);
};
// Eventos websocket
useEffect(() => {
if (websocket) {
websocket.on("call", pass => {
setPasswords(passwords.filter(value => value.id !== pass.id));
});
}
});
// Carrega senhas na fila
useEffect(() => {
api.get("/passwords?status=1", apiHeader(cookies.token)).then(res => {
setPasswords(res.data);
});
}, [cookies.token]);
return (
<main className="adminPageBody">
{menuOpen && <ModalCaller setState={setMenuOpen} pass={pass} />}
<h3>Aguardando ({passwords.length})</h3>
{passwords.map((pass, i) => (
<div className="card" key={i}>
<div>
<h4>{pass.fullPassword}</h4>
<p>{pass.services.name}</p>
<p>{pass.dob}</p>
</div>
<button onClick={() => handleToggleModalCaller(true, pass)}>
Chamar
</button>
</div>
))}
</main>
);
};
export default WaitingPage; |
Is is so bad to leave this warning ? Does it really impact performances ? I've been wondering about the case where you would want to dispatch actions anyways (for example after a PUT/POST request where you want to update your context even if the component umounts). Then you would have to handle a "cancel logic" in your context (in case your context is unmounted). I tried it in the "Fetch and store in a context" example here. This looks painful though, shouldn't we be able to tell react to ignore the call and not have a warning ? |
The solution to this problems it to use a flag to check if a component is unmounted, avoid setting state update, as the following in a useEffect hook. let ignore = false;
.
.
.
if (!ignore)
setServerError(errors);
return () => {
ignore = true;
} |
Yes but what if you're dispatching actions to a context and want to dispatch even if the component unmounts ? For exemple you have a popin with a form that change the color of your website. You would still want to update the website color even if the user has closed the popin while the API call was still being processed |
How about exposing (exporing) a setColor from a top level component that is always mounted, and use this setColor inside popin component? |
Well yeah this would work but that's not the point, you could have a context that is not always mounted. The question being "What happens when react prints this warning", is it harmful in some way ? If so how should we handle a |
Esto me ayudo mucho https://www.debuggr.io/react-update-unmounted-component/ |
|
@TomPradat What's harmful is the lack of one's thought alignment with |
I'm not circling around the warning, I'm trying to understand if there is an idiomatic way to handle this that i would have missed. I don't want to overly complicate my apps for edge cases that will rarely happen. Though I'm interested what ppl think about those edge cases. What's more I'm not sure that "wanting to update a context after a POST request" is such a rare use case. I'll take a look at the |
I get the impression that an In practice, this could only be an issue if an impatient user navigates away just as the data comes in. |
I dont understand the solutions given here. Am also facing same issue |
It is obvious that if this simple trick makes the difference it's because React is bad designed. They cared so much about the management of the state that the state ended up managing them. |
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment! |
Just got the same warning today. I will try to implement the solution proposed here. My code is: const WarehouseTab = () => {
} export default WarehouseTab and the message: Using Axios to setState |
Super common problem to call an api async and want to update state with use effect .... not sure how this is still misunderstood.. that seems like every single app would use that. There are TONS of blog posts about about how to solve for this. Here is one I thought was well done: https://www.debuggr.io/react-update-unmounted-component/ SO has literally dozens or who knows maybe even more questions. Here is a recent example: I did some digging into this. As mentioned/demonstrated, this is a VERY common problem. The logic shown above links for solving this can be encapsulated nicely into a hook / npm module. See here for one example (there are many with all very similar names) :https://github.com/helderburato/use-is-mounted-ref/blob/main/src/use-is-mounted-ref.ts One would naturally lean on the amazing react team and their docs for clarity on best practice. But that could lead to confusion for this issue in my view see here reactjs/react.dev#1082. Not sure if this is directly related or not #22114 but I can't seem to find this warning we are all talking about in the react source code to inspect further. So maybe they took it out. In fact my team had been using 3 different variations of the aforementioned solutions which I found while tasked to refactor parts of for various reasons. I was still seeing the warning in the console even after applying strategies aforementioned. Then I tried this one which is same as the others yet goes even further to add a useCallback in there at the end. It seems to be working the best: https://github.com/hupe1980/react-is-mounted-hook/blob/master/src/use-is-mounted.tsx
|
Given that the latest release was 17.0.2 (March 22, 2021) and this warning was removed on Aug 18 #22114 -- this issue is not going to be an issue someday based on that eh |
React 18 does not have this warning because it's often pointless. (Such as in this case.) |
Do you want to request a feature or report a bug?
Reporting a possible bug
What is the current behavior?
My app renders fine with no errors but I can't seem to figure out why I keep getting this warning:
index.js:1446 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
in ArtistProfile (at App.js:51)
in component (created by Route)
api-calls.js (Here's a link):
https://github.com/ryansaam/litphum/blob/master/src/api-calls.js
App.js
ArtistProfile.js
What is the expected behavior?
If you can see in ArtistProfile.js I am using a clean up function that aborts when the component does unmount. The fetch would be aborted and state shouldn't update but for some reason I am still getting this memory leak warning.
What I am expecting is for the warning to no longer throw because am using a clean up function that aborts the fetch.
Link to repo: https://github.com/ryansaam/litphum
App.js: https://github.com/ryansaam/litphum/blob/master/src/App.js
ArtistProfile.js: https://github.com/ryansaam/litphum/blob/master/src/components/ArtistProfile.js
api-calls.js: https://github.com/ryansaam/litphum/blob/master/src/api-calls.js
My stackoverflow question: https://stackoverflow.com/questions/54954385/react-useeffect-causing-cant-perform-a-react-state-update-on-an-unmounted-comp/54964237#54964237
Which versions of React, and which browser
React 16.8.2
Latest version of Chrome
The text was updated successfully, but these errors were encountered: