Everyone who has worked in React for a while remembers a lot of guidelines to avoiding the following warning: ⚠
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
💡This warning arises in case of an asynchronous operation tries to update a state of an unmounted component.
To hide this warning we used to apply the following solution by checking if component is mounted or not:
const isMountedRef = useRef(false)
useEffect(() => {
isMountedRef.current = true
const getUserData = async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
const data = (await response.json()) as UserInfo;
if (isMountedRef.current) {
if (data.name) {
setUserInfo(data);
} else setUserInfo(null);
}
};
getUserData();
return () => {
isMountedRef.current = false
};
}, [userId]);
🌟However, since React 18, the React team has removed this warning (when calling setState on an unmounted component), because this warning is widely misunderstood and is can be misleading. So, you can write your useEffect as follows without fear of memory leak, also, since you are using Promises then the previous warning will not be triggered.
useEffect(() => {
const getUserData = async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
const data = (await response.json()) as UserInfo;
if (data.name) {
setUserInfo(data);
} else setUserInfo(null);
};
getUserData();
}, [userId]);
👉On the other hand, avoiding adding isMounted checking will be relevant for upcoming React versions because there may be some features that preserve the DOM and its state even when the component is not mounted (not visible).
💻To verify this situation from yourside, you can use the following code which simulate the situation in which component was unmounted by the time the response was received.
import React, { useEffect, useState } from "react";
type UserInfo=Record<string, any>;
export const AppContainer = () => {
const [show, setShow] = useState(true);
useEffect(() => {
/// UserDashboard will unmount after 3000 milliseconds
const intervalId = setTimeout(() => setShow(false), 3000);
return () => {
clearTimeout(intervalId);
};
}, []);
return show ? <UserDashboard /> : <div>no items to show</div>;
};
const UserDashboard = () => {
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
useEffect(() => {
const getUserData = async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/1`
);
const data = (await response.json()) as UserInfo;
if (data.name) {
setUserInfo(data);
} else setUserInfo(null);
};
/// The response will take at least 4000 milliseconds
const intervalId = setTimeout(getUserData, 4000);
// return ()=>{clearTimeout(intervalId)}
}, []);
return userInfo ? (
<UserCard userInfo={userInfo} />
) : (
<div className="not-found">No user found, Check user Id</div>
);
};
type UserCardProps={userInfo: UserInfo}
const UserCard = ({ userInfo }: UserCardProps) => {
return (
<div>
id:{userInfo?.id}
<br />
Name:{userInfo?.name}
<br />
Address:
{`${userInfo?.address?.city} ${userInfo?.address?.zipcode}, ${userInfo?.address?.street}`}
</div>
);
};
export default UserDashboard;
You will have no warnings in your console.
Top comments (1)
Thanks for sharing 🤠