You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React使用useEffect时出现无法为已卸载组件更新状态警告如何解决

问题原因

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中触发的isUserAuthenticated异步校验请求还未完成时,ProtectedRoute组件已经被卸载(比如路由跳转到登录页、用户主动切走路由等场景),请求完成后仍然尝试调用setIsAuthenticated更新已卸载组件的状态,既无实际意义也会造成内存泄漏。

另外现有代码还有两个可优化点:

  • useEffect没有传入依赖数组,会导致每次组件渲染都重复触发身份校验请求,造成不必要的性能损耗
  • 没有针对异步任务做卸载前的清理逻辑
修复方案

修改ProtectedRoute.tsx中的useEffect逻辑,新增组件挂载状态标记,仅在组件仍挂载时执行状态更新,同时补全依赖数组:

export function ProtectedRoute({ ...routeProps }: ProtectedRouteProps): ReactElement | null {
    const context = useContext(AppContext);
    const [isAuthenticated, setIsAuthenticated] = useState(false);

    useEffect(() => {
        // 标记组件是否处于挂载状态
        let isMounted = true;
        
        isUserAuthenticated(context.token)
            .then(res => {
                // 仅组件仍挂载时更新状态
                if (isMounted) setIsAuthenticated(res);
            })
            .catch(() => {
                if (isMounted) setIsAuthenticated(false);
            });
        
        // 清理函数:组件卸载时修改标记位
        return () => {
            isMounted = false;
        };
    // 依赖数组:仅当token变化时才重新触发校验
    }, [context.token]);

    if (isAuthenticated) {
        return <Route {...routeProps} />;
    } else {
        return <Redirect to={{ pathname: "login" }} />;
    }
}

const isUserAuthenticated = async (token: any): Promise<boolean> => {
    try {
        const response = await VerifyAuthentication(token);
        console.log("VerifyAuthentication", response);
        return response.status === 200;
    } catch (err) {
        return false;
    }
};
可选优化(请求中止)

如果VerifyAuthentication是基于fetch实现的接口请求,你也可以用AbortController在组件卸载时直接中止未完成的请求,比单纯标记位更彻底:

useEffect(() => {
    const controller = new AbortController();
    
    const checkAuth = async () => {
        try {
            // VerifyAuthentication方法需要支持接收AbortSignal参数
            const response = await VerifyAuthentication(context.token, controller.signal);
            setIsAuthenticated(response.status === 200);
        } catch (err) {
            // 跳过请求中止抛出的错误,不需要更新状态
            if (err.name !== 'AbortError') {
                setIsAuthenticated(false);
            }
        }
    }
    checkAuth();
    
    return () => {
        controller.abort();
    };
}, [context.token])

内容的提问来源于stack exchange,提问作者Kay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 06:15:02