React Router路由切换异常:组件不渲染+卸载组件状态更新警告
看起来你的问题主要出在两个地方:重复嵌套的Router组件导致子路由无法渲染,以及SignIn组件卸载后未清理异步任务引发的内存泄漏警告。我来一步步帮你解决:
1. 移除Player组件中的重复Router
整个应用只需要一个Router实例(你已经在最外层的Routes组件里声明了),嵌套的<Router>会创建一个独立的路由上下文,导致内部的useRouteMatch和Route无法和外层的路由状态同步,这就是FillCollect组件无法渲染的核心原因。
修改后的Player组件代码:
import React from "react"; import { Switch, Route, useRouteMatch } from "react-router-dom"; import Navigation from "./Navigation/PlayerNavigation"; import FillCollect from "./Containers/Collect/Collect"; import Dashboard from "./Containers/Dashboard/Dashboard"; const Player = () => { const { path } = useRouteMatch(); console.log(path); // 现在能正确输出 "/app/player" return ( <> <Navigation /> <Switch> {/* 建议添加一个默认的Dashboard路由,避免进入Player后页面空白 */} <Route exact path={path} component={Dashboard} /> <Route path={`${path}/collect/:id`} component={FillCollect} /> </Switch> </> ); }; export default Player;
2. 修复SignIn组件的内存泄漏警告
这个警告是因为你登录跳转后,SignIn组件被卸载了,但组件内部可能还有未完成的异步操作(比如登录API请求),之后尝试更新组件状态导致的。解决方法是在组件卸载时清理这些异步任务:
方法一:用ref标记组件挂载状态
如果是用原生fetch或者普通异步操作,可以用ref来跟踪组件是否还挂载,只在挂载时更新状态:
import React, { useState, useEffect, useRef } from "react"; import { useHistory } from "react-router-dom"; const SignIn = () => { const [loading, setLoading] = useState(false); const history = useHistory(); const isMounted = useRef(true); // 组件卸载时标记为已卸载 useEffect(() => { return () => { isMounted.current = false; }; }, []); const handleLogin = async () => { setLoading(true); try { // 假设这里是你的登录请求 const response = await fetch("/api/login", { method: "POST", body: JSON.stringify({ /* 登录参数 */ }) }); const data = await response.json(); // 只有组件还挂载时才更新状态和跳转 if (isMounted.current) { setLoading(false); history.push("/app/player"); } } catch (error) { if (isMounted.current) { setLoading(false); console.error("登录失败:", error); } } }; return ( <div className="sign-in-container"> {/* 你的登录表单 */} <button onClick={handleLogin} disabled={loading}>登录</button> </div> ); }; export default SignIn;
方法二:用CancelToken取消axios请求(如果用axios的话)
如果你的登录请求用的是axios,可以用CancelToken来彻底取消未完成的请求,更优雅:
import React, { useState, useEffect } from "react"; import { useHistory } from "react-router-dom"; import axios from "axios"; const SignIn = () => { const [loading, setLoading] = useState(false); const history = useHistory(); let cancelTokenSource; useEffect(() => { // 组件卸载时取消请求 return () => { if (cancelTokenSource) { cancelTokenSource.cancel("组件已卸载,取消登录请求"); } }; }, []); const handleLogin = async () => { setLoading(true); cancelTokenSource = axios.CancelToken.source(); try { const response = await axios.post("/api/login", { /* 登录参数 */ }, { cancelToken: cancelTokenSource.token }); setLoading(false); history.push("/app/player"); } catch (error) { if (!axios.isCancel(error)) { setLoading(false); console.error("登录失败:", error); } } }; return ( <div className="sign-in-container"> <button onClick={handleLogin} disabled={loading}>登录</button> </div> ); }; export default SignIn;
3. 额外优化:统一路由路径格式
建议把外层Routes组件中Player的路由路径末尾的斜杠去掉,避免匹配时的歧义:
// Routes组件里的Player路由 <Route path="/app/player" component={Player} />
这样修改后,应该就能正常渲染FillCollect组件,同时消除内存泄漏的警告了。
内容的提问来源于stack exchange,提问作者cocofcb6
相关产品推荐
相关产品推荐

