我收到错误:即使我创建了清理,也无法对未挂载的组件执行 React 状态更新



一个错误一直困扰着我的应用程序,上面写着

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.

我确实在我的上下文中声明了一个useEffect,这样我就可以为我的应用程序实时存储和获取数据。

这是我在Context中的代码;

const FetchProvider = ({ children }) => {
const [userList, setUserList] = useState([]);
const [teamList, setTeamList] = useState([]);
const authContext = useContext(AuthContext);
const authAxios = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
useEffect(() => {
let isCancelled = false;
if (
authContext.isAuthenticated &&
authContext.authState.userInfo !== null
) {
const getUsers = async () => {
try {
const users = await authAxios.get('/admin/get-all-users');
if (!isCancelled) {
setUserList(users.data);
}
} catch (error) {
if (!isCancelled) {
console.log(error);
}
}
};
const getTeams = async () => {
try {
const teams = await authAxios.get('/get-all-teams');
if (!isCancelled) {
setTeamList(teams.data);
}
} catch (error) {
if (!isCancelled) {
console.log(error);
}
}
};
getUsers();
getTeams();
}
return () => {
isCancelled = true;
};
}, [authAxios, authContext]);
return (
<Provider
value={{
authAxios,
userList,
setUserList,
teamList,
setTeamList,
}}
>
{children}
</Provider>
);
};

我在我的Login.jsx和我的中收到了这个错误,尽管我在提交和声明时没有声明useEffect。

这是我的密码;

const submitCredentials = async (credentials, resetForm) => {
try {
setLoginLoading(true);
const { data } = await publicFetch.post('signin', credentials);
authContext.setAuthState(data);
setSignupSuccess(data.message);
setSignupError('');
setOpen(true);
setTimeout(() => {
setLoginLoading(false);
setredirectOnlogin(true);
resetForm(true);
}, 400);
} catch (error) {
setLoginLoading(false);
const { data } = error.response;
setSignupError(data.message);
setSignupSuccess('');
setOpen(true);
}
return setLoginLoading(false);
};

我已经尝试了很多互联网提供的方法来解决这个问题,但不幸的是,它并没有解决我的问题。

我在我的UsersTable.jsx和TeamTables.jsx.中确实有useEffect

这是我在UsersTable.jsx中的代码;

useEffect(() => {
let isCancelled = false;
const getUsers = async () => {
try {
const users = await fetchContext.authAxios.get('/admin/get-all-users');
setIsLoaded(true);
if (isLoaded === true) {
if (!isCancelled) {
fetchContext.setUserList(users.data);
}
}
return () => {
isCancelled = true;
};
} catch (error) {
if (!isCancelled) {
console.log(error);
}
}
};
getUsers();
return () => {
isCancelled = true;
};
}, [fetchContext, isLoaded]);

这是我的TeamTable.jsx中的useEffect代码;

useEffect(() => {
let isCancelled = false;
const getTeams = async () => {
try {
const teams = await fetchContext.authAxios.get('get-all-teams');
setIsLoaded(true);
if (isLoaded === true) {
if (!isCancelled) {
fetchContext.setTeamList(teams.data);
}
}
} catch (error) {
if (!isCancelled) {
console.log(error);
}
}
};
getTeams();
return () => {
isCancelled = true;
};
}, [fetchContext, isLoaded]);

isLoaded用作AJAX

好吧,您可以使用React推荐的方法来解决这个问题。您所需要做的就是将您的api调用封装在makeCancelable方法中,并在组件卸载时取消它们。

参考编号:https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html

要做到这一点,请创建

const makeCancelable = (promise) => {
let isCancelled = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
val => isCancelled ? reject({isCanceled: true}) : resolve(val),
error => isCancelled ? reject({isCanceled: true}) : reject(error)
);
});
return {
promise: wrappedPromise,
cancel() {
isCancelled = true;
},
};
};

useEffect之外的请求创建一个变量

let fetchTeamsRequest = null;

并更新了您的useEffect函数,如下所示。

useEffect(() => {
const getTeams = async () => {
if (fetchTeamsRequest) {
try {
await fetchTeamsRequest.promise;
return;
} catch (error) {
return;
}
}
fetchTeamsRequest = makeCancellable(fetchContext.authAxios.get('get-all-teams'));
try {
const teams = await fetchTeamsRequest.promise;
fetchTeamsRequest = null;
setIsLoaded(true);
if (isLoaded === true) {
if (!fetchTeamsRequest.isCancelled) {
fetchContext.setTeamList(teams.data);
}
}
} catch (error) {
if (!fetchTeamsRequest.isCancelled) {
fetchTeamsRequest = null;
console.log(error);
}
}
};
getTeams();
return () => {
if (fetchTeamsRequest) {
fetchTeamsRequest.cancel();
}
};
}, [fetchContext, isLoaded]);

最新更新