React 和 antd:路由器不会重新渲染组件

Posted

技术标签:

【中文标题】React 和 antd:路由器不会重新渲染组件【英文标题】:React and antd: Router doesn't re-render components 【发布时间】:2021-02-25 10:54:43 【问题描述】:

我有一个带有登录和搜索页面的简单网页。我在顶部还有一个导航栏,可以在两者之间切换。基本的App.js 如下所示:

const history = createBrowserHistory();

function App() 
    return (
        <Router history=history>
            <CustomLayout>
                <Switch>
                    <BaseRouter/>
                </Switch>
            </CustomLayout>
        </Router>
    );


export default App;

现在,BaseRouterCustomLayout 只是

const BaseRouter = () => (
    <div>
        <Route exact path="/list" component=ItemsList/>
        <Route path="/login" component=LoginForm/>
    </div>
);

export default BaseRouter;

const CustomLayout = (children) => 
    return(
        <>
        <Navbar/>
        children
        </>
    );


export default CustomLayout;

现在,导航栏看起来像这样

import React from "react";
import Menu from 'antd';
import Link from "react-router-dom";

 const Navbar = () => 
    return (
        <div>
            <Menu mode="horizontal" theme="dark">
                <Menu.Item key="list">
                    <Link to="/list">List</Link>
                </Menu.Item>
                <Menu.Item key="login">
                    <Link to="/login">Sign in</Link>
                </Menu.Item>
            </Menu>
        </div>
    );


export default Navbar

让我们保持组件简单:

const Login = () => 
    return (
        <div>
            login
        </div>
    );


export default Login

const List = () => 
    return (
        <div>
            list
        </div>
    );


export default List

现在的问题是,当我单击导航栏中的链接时,即使路线发生变化,React 也不会重新渲染组件。我已经在 SO 上看到了数百个答案,但我仍然无法弄清楚。注意 避免刷新或重新加载页面对我来说很重要。编辑 奇怪的是,当我将 Router 更改为 BrowserRotuer 时,它工作正常,但我不能使用我自己的历史记录。

【问题讨论】:

【参考方案1】:

为什么不使用 react-router-dom 包中的 BrowserRouter

App.js:- 使用来自react-router-domBrowserRouter
import  BrowserRouter as Router, Switch  from 'react-router-dom'

function App() 
  return (
    <Router>
      <CustomLayout>
        <Switch>
          <BaseRouter/>
        </Switch>
      </CustomLayout>
    </Router>
  );


export default App;
BaseRouter.js:- 从react-router-dom 导入Route
import  Route  from 'react-router-dom'

const BaseRouter = () => (
  <div>
    <Route exact path="/list" component=ItemsList/>
    <Route path="/login" component=LoginForm/>
  </div>
);

export default BaseRouter;
Navbar.js:-
import React from "react";
import Menu from 'antd';
import Link from "react-router-dom";

const Navbar = () => 
  return (
    <div>
      <Menu mode="horizontal" theme="dark">
        <Menu.Item key="list">
          <Link to="/list">List</Link>
        </Menu.Item>
        <Menu.Item key="login">
          <Link to="/login">Sign in</Link>
        </Menu.Item>
      </Menu>
    </div>
  );


export default Navbar

那么如果你想使用history:-

import  useHistory  from 'react-router-dom'

const testFunctionComponent = () => 
  const history = useHistory()
  
  const handleClick = (urlPath) => 
    // you can do
    history.push(urlPath) // to go anywhere 
  

  return (
    <>
      <button onClick=() => handleClick('/anypath')>Click Me!<button>
    </>
  )

【讨论】:

致任何阅读本文的人:这是最简单的方法。我还发现如果你使用BrowserRouter,你可以在你喜欢的任何组件中调用props.history.push() 是的。对于作为BrowserRouter 下的子组件的所有组件都是如此。【参考方案2】:

从此更改您的BaseRouter

const BaseRouter = () => (
    <div>
        <Route exact path="/list" component=ItemsList/>
        <Route path="/login" component=LoginForm/>
    </div>
);

export default BaseRouter;

到这里:

const BaseRouter = () => (
    <div>
        <Route exact path="/list" component=ItemsList/>
        <Route path="/login" component=LoginForm/>
    </div>
);

export default BaseRouter;

【讨论】:

不,很抱歉,这并没有改变什么【参考方案3】:

我相信您不能将 div 放在开关内。您没有将 Route 组件暴露给您的 switch 语句。

因此,您的 url 会发生变化,因为您的导航栏会使其发生变化,但您的开关不知道该怎么做。

尝试将您的基本路由器更改为:

const history = createBrowserHistory();

function App() 
    return (
        <Router history=history>
            <CustomLayout>

                    <BaseRouter/>

            </CustomLayout>
        </Router>
    );


export default App;
const BaseRouter = () => (
    <Switch>
        <Route exact path="/list" component=ItemsList/>
        <Route path="/login" component=LoginForm/>
    </Switch>
);

export default BaseRouter;

【讨论】:

不,很抱歉,这并没有改变什么【参考方案4】:

Router 是低级的,希望您管理事物。 BrowserRouter 已经与 html5 历史同步。如果您想要Router,我认为您必须自己管理同步(例如&lt;Link onClick=() =&gt; history.push(href)&gt;) 或收听历史记录以进行更改检测。请参阅Detect Route Change with react-router

【讨论】:

这是否解释了我收到警告的原因:You cannot change the history of Router 这是真的,但还有更简单的方法,例如useLocation钩子

以上是关于React 和 antd:路由器不会重新渲染组件的主要内容,如果未能解决你的问题,请参考以下文章

antd 覆盖样式化组件

使用 React Router 重新加载 Webpack HotModule 渲染与组件

React 不会在路由参数更改或查询更改时重新加载组件数据

如何在 react 中的新路由更改上使用 useEffect 重新渲染变量

react多个路由共用同一个组件模块,切换路由跳转页面不刷新问题

为啥在 React 中,当父组件重新渲染时子组件不会重新渲染(子组件没有被 React.memo 包裹)?