如何修复React报错:Can't perform state update on unmounted component
问题修复方案
根因分析
触发警告和异常的核心原因有3个:
- App组件内的异步请求没有添加清理逻辑,若组件卸载时请求仍在进行,后续dispatch操作会触发「卸载组件更新状态」的警告
- 登录状态更新顺序错误:先将
isLogged设为true触发Home组件渲染,再请求用户信息,导致Home组件初始渲染时auth.user为空数组,直接取auth.user[0].username会出现取值异常 - Home组件没有做空值兼容,
auth.user为空时直接索引取值会抛出隐性错误,也会连带触发状态更新异常警告
具体修复步骤
1. 修正App.tsx的异步逻辑与清理机制
调整登录状态更新顺序,同时为异步请求添加卸载清理逻辑,避免无效dispatch:
function App() { const dispatch = useDispatch(); const auth = useSelector((state: RootStateOrAny) => state.auth); const token = useSelector((state: RootStateOrAny) => state.token); useEffect(() => { // 添加请求取消控制器,组件卸载时终止未完成的请求 const controller = new AbortController(); const firstlogin = localStorage.getItem('firstlogin'); if(firstlogin) { const getUserToken = async () => { try { const res: any = await axios.post('/user/refresh_token', null, { signal: controller.signal }); dispatch({type: "GET_TOKEN", payload: res.data.access_token}); } catch (err) { // 忽略取消请求的报错 if (axios.isCancel(err)) return; } }; getUserToken(); } // 清理函数:组件卸载时终止请求 return () => controller.abort(); }, [auth.isLogged, dispatch]); useEffect(() => { // 标记组件是否挂载,避免卸载后执行dispatch let isMounted = true; if(token){ const getUser = async () => { try { // 先请求用户信息,请求成功再更新登录状态 const res = await fetchUser(token); if (isMounted) { dispatch(dispatchGetUser(res)); dispatch(dispatchLogin()); } } catch (err) { // 可在这里添加token过期的清理逻辑,比如清空本地存储跳登录页 } } getUser(); } // 清理函数:标记组件已卸载 return () => { isMounted = false }; },[token, dispatch]); return ( <Router> <div className="App"> <Header/> <Body/> </div> </Router> ); } export default App;
2. 给Home组件添加空值兼容
使用可选链和兜底值避免空值报错:
import React from 'react'; import { useSelector, RootStateOrAny } from 'react-redux'; export default function Home() { const auth = useSelector((state: RootStateOrAny) => state.auth); // 可选链+兜底值兼容空数据场景 const username = auth.user?.[0]?.username || ''; return( <div> {username ? `Welcome ${username} !` : '用户信息加载中...'} </div> ) };
3. 优化Body组件的路由判断(可选,避免闪屏)
添加加载状态,避免校验登录态时出现页面闪跳:
export default function Body() { const auth = useSelector((state: RootStateOrAny) => state.auth); const token = useSelector((state: RootStateOrAny) => state.token); const {isLogged} = auth; // 登录态校验中显示加载提示 if (token && !isLogged) { return <div>登录状态校验中...</div> } return ( <Switch> <Route path="/signup"> {isLogged ? <Notfound/> : <Signup/>} </Route> <Route path="/login"> {isLogged ? <Notfound/> : <Login/>} </Route> <Route path="/home"> {isLogged ? <Home/> : <Login/>} </Route> <Redirect to="/home"/> </Switch> ) }
内容的提问来源于stack exchange,提问作者user15610239
相关产品推荐
相关产品推荐

