使用react-router-dom的Switch时,更新路由查询字符串致组件卸载
问题:保留Switch功能同时避免更新查询字符串时卸载组件
我的App.js中用react-router-dom的Switch包裹了多个组件,当通过history.push更新查询字符串时,当前组件会触发卸载(移除Switch则不会),导致数据加载出问题。希望保留Switch的同时解决这个问题。
App.js
import React from "react"; import Loadable from "react-loadable"; import { Switch } from "react-router-dom"; import ProtectedRoute from "./ProtectedRoute"; const Test = Loadable({ loader: () => import("./Test"), loading: () => <h1>LOADING....</h1> }); const Test1 = Loadable({ loader: () => import("./Test1"), loading: () => <h1>LOADING....</h1> }); const Test2 = Loadable({ loader: () => import("./Test2"), loading: () => <h1>LOADING....</h1> }); const App = () => { return ( <Switch> <ProtectedRoute bgColour="blue" exact path="/" component={Test} /> <ProtectedRoute bgColour="red" exact path="/1" component={Test1} /> <ProtectedRoute bgColour="green" exact path="/2" component={Test2} /> </Switch> ); }; export default App;
ProtectedRoute.js
ProtectedRoute会渲染react-router-dom的Route组件,传入指定component,包含用于身份验证的HOC:
import React from "react"; import { Route } from "react-router-dom"; const withAuth = (Component) => { return (props) => { return <Component {...props} />; }; }; const ProtectedRoute = ({ component, bgColour, ...args }) => { return ( <div style={{ backgroundColor: bgColour || "transparent" }}> <Route component={withAuth(component)} {...args} /> </div> ); }; export default ProtectedRoute;
Test.js
组件挂载/卸载时有alert提示,点击按钮更新查询字符串会触发卸载:
import React, { useEffect } from "react"; import { useHistory } from "react-router-dom"; export default function Test() { const history = useHistory(); useEffect(() => { alert("MOUNTED BASE"); return () => { alert("UNMOUNTED BASE"); }; }, []); return ( <div> <h1>TEST COMPONENT BASE - BLUE</h1> <div onClick={() => history.push({ pathname: history.location.pathname, search: `?query=${Math.random().toFixed(2)}` }) } > UPDATE QUERY STRING </div> <div onClick={() => history.push("/1")}>GO TO Next ROUTE</div> </div> ); }
解决方案
问题根源是ProtectedRoute中每次渲染都会通过withAuth(component)创建新的组件实例,Switch搭配Route的component属性时,会认为组件发生变更,从而触发卸载/挂载流程。以下两种方案可解决:
方案1:稳定组件引用
用useMemo缓存withAuth包装后的组件,确保每次渲染时引用一致:
import React from "react"; import { Route } from "react-router-dom"; const withAuth = (Component) => { return (props) => { return <Component {...props} />; }; }; const ProtectedRoute = ({ component: Component, bgColour, ...args }) => { // 缓存包装后的组件,避免每次渲染创建新实例 const AuthComponent = React.useMemo(() => withAuth(Component), [Component]); return ( <div style={{ backgroundColor: bgColour || "transparent" }}> <Route component={AuthComponent} {...args} /> </div> ); }; export default ProtectedRoute;
方案2:改用Route的render属性
Route的render属性不会像component那样在每次渲染时创建新组件实例,直接在render回调中渲染授权组件:
import React from "react"; import { Route } from "react-router-dom"; const withAuth = (Component) => { return (props) => { return <Component {...props} />; }; }; const ProtectedRoute = ({ component: Component, bgColour, ...args }) => { return ( <div style={{ backgroundColor: bgColour || "transparent" }}> <Route {...args} render={(routeProps) => <withAuth(Component) {...routeProps} />} /> </div> ); }; export default ProtectedRoute;
内容的提问来源于stack exchange,提问作者mcclosa
相关产品推荐
相关产品推荐

