I'm new to React/Node and working on a learning project. It's a platform that connects users (freelancers) with nonprofit companies. When a user logs in, they can go to the UserConnections page to view all the companies they connected with.
This is working correctly, but I also get the 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.
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.
Also when I visit this component, the command line starts spitting out connection messages extremely fast endlessly.
I think the problem is coming from this useEffect
that is used in the UserConnections component. This block of code was a pain to write, and it took the help of multiple people on here to write it. I'm hesitant to touch it.
useEffect(() => {
const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
Promise.all(comps).then((comps => setCompanies(comps)));
}
}, [connections]);
I tried to rewrite it like so:
useEffect(() => {
let isMounted = true;
const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
if (isMounted) {
Promise.all(comps).then((comps => setCompanies(comps)));
}
return () => { isMounted = false };
}, [connections]);
But that didn't change anything at all.
How can I change this code to avoid the memory leak without affecting the intended use of this function? Below is the code for this component. Thank you for any help.
UserConnections
function UserConnections() {
const { currentUser, connectionHandles } = useContext(UserContext);
const connections = currentUser.connections.concat(connectionHandles);
const [companies, setCompanies] = useState([]);
useEffect(() => {
const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
Promise.all(comps).then((comps => setCompanies(comps)));
}, [connections]);
if (!companies || companies.length == 0) {
return (
<div>
<p>You have no connections</p>
</div>
)
} else {
return (
<div>
{companies && companies.map(c => (
<CompanyCard
key={c.companyHandle}
companyHandle={c.companyHandle}
companyName={c.companyName}
country={c.country}
numEmployees={c.numEmployees}
shortDescription={c.shortDescription}
/>
))}
</div>
);
}
};
You got it almost right in your rewrite (great job!) - the isMounted
check needs to go inside the then
.
instead of:
if (isMounted) {
Promise.all(comps).then((comps => setCompanies(comps)));
}
you write it like this:
Promise.all(comps).then(comps => isMounted && setCompanies(comps));
It's also just a warning. You should fix it, yes, but it's not crazy critical.
EDIT:
The infinite loop problem:
const connections = currentUser.connections.concat(connectionHandles);
connections changes everytime you re-render your component (call setCompanies
) so the useEffect
reference changes. The way to fix it is to use connectionHandles
and currentUser
as dependencies of the effect, and move connections
inside of the effect like:
function UserConnections() {
const { currentUser, connectionHandles } = useContext(UserContext);
const [companies, setCompanies] = useState([]);
useEffect(() => {
let isMounted = true;
// move connections inside the effect
const connections = currentUser.connections.concat(connectionHandles);
const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
Promise.all(comps).then((comps => isMounted && setCompanies(comps)));
return () => { isMounted = false };
}, [currentUser,connectionHandlers]);
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.