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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 22:09:23