【问题标题】:Prevent react-router history.push from reloading current route防止 react-router history.push 重新加载当前路由
【发布时间】:2016-10-20 04:39:28
【问题描述】:

我正在通过react-router 迈出第一步。

我目前正在使用hashHistory 进行开发,并且正在执行“手动”导航。也就是说,我使用Link,而是调用history.push('/some/route');来导航(以响应锚标记上的普通点击)。

我注意到的是,即使我已经在目标路线上,react-router 也会在每次调用 history.push('/target/route'); 时重新渲染相关的目标组件:在每个 push('/target/route') 上:

  • URL 的片段部分仍然是#/target/route
  • URL 的查询字符串部分更改为?_k=somethingRandom
  • 目标组件重新渲染

我希望重新渲染不会发生 - 我实际上希望 history.push 在我已经在尝试 @987654331 的路线上时成为空操作@。

我显然遗漏了一些东西,因为这不是正在发生的事情。有趣的是,我看到那些试图实现我想摆脱的行为的人的帖子 - 他们想“刷新”一条路线而不离开它,可以这么说.这看起来很像相反的问题:)。

您能否告诉我我的误解是什么以及如何实现预期的行为?如果(何时)我切换到browserHistory,这可能会消失吗?

【问题讨论】:

    标签: javascript reactjs react-router


    【解决方案1】:

    我认为更简单的解决方法可能是用我们自己的路线替换路线

    import { Route, withRouter } from "react-router-dom";
    
    function MyRoute({ key, path, exact, component: Component, history }) {
      let lastLocation = null;
      return (
        <Route
          key={key}
          path={path}
          exact={exact}
          render={(props) => {
            history.listen((location) => {
              lastLocation = location;
            });
    
            // monkey patching to prevent pushing same url into history stack
            const prevHistoryPush = history.push;
            history.push = (pathname, state = {}) => {
              if (
                lastLocation === null ||
                pathname !==
                  lastLocation.pathname + lastLocation.search + lastLocation.hash ||
                JSON.stringify(state) !== JSON.stringify(lastLocation.state)
              ) {
                prevHistoryPush(pathname, state);
              }
            };
            return <Component {...props} />;
          }}
        />
      );
    }
    
    export default withRouter(MyRoute);
    

    我们使用它作为 react-router-dom 的实际 Route 的包装器,它对我来说非常有效。 更多请参考here

    【讨论】:

    • 此解决方案可能有效,但请注意:const prevHistoryPush = history.push; 在渲染循环中,这意味着它会在每次渲染时添加对堆栈的调用。这是内存泄漏,最终会导致函数深度错误。
    【解决方案2】:

    tsx 样本

    import {createBrowserHistory} from 'history';  
    export const history = createBrowserHistory();
    
    
    ReactDOM.render(
      <Router history={history}>
        <App/>
      </Router>,
      document.getElementById("root")
    );
    

    【讨论】:

      【解决方案3】:

      我会试一试...

      如果您登陆这里并希望更改您的 URL(例如出于共享目的),那么 RR docs 已经有了描述的解决方案。只要确保您不使用组件中的历史记录(即this.props.history.push()),因为您将(如预期)被路由到目标。但是,您可以访问your 浏览器历史记录,而不会干扰组件的history

      以下仅在 Chrome 上测试

      // history.js
      import { createBrowserHistory } from 'history'
      export default createBrowserHistory()
      

      然后从你的 XYZ 组件中

      // XYZ.js
      import React from 'react';
      import history from './history'
      
      class XYZ extends React.Component {
          _handleClick() {
              // this should not cause rerender and still have URL change 
              history.push("/someloc");
          }
          render() {
              return(
                <button onClick={this._handleClick.bind(this)}>test </button>
              )
          }
      }
      

      希望对其他人有所帮助。

      【讨论】:

        【解决方案4】:

        我有同样的问题,我找到了(愚蠢的)解决方案。

        你只有一个&lt;button&gt;(默认按钮是type=submit)或类似的东西(表单、提交......等),它就像一个html &lt;form method=GET ...&gt;一样重新加载页面。

        在您的代码中检查它,然后将其删除。

        PD: _k=somethingRandom > 这只是您在表单中发送的值输入(或按钮)。

        【讨论】:

          【解决方案5】:

          App.js:

          shouldComponentUpdate(nextProps, nextState) {
            return nextProps.location.search === this.props.location.search
          }
          

          【讨论】:

            【解决方案6】:

            我的猜测是您的组件会重新渲染,因为当您进行路由器推送时,您的 prop 中的某些内容会发生变化。我怀疑它可能是prop.locationactionkey 属性。您可以在每次渲染期间检查prop 的所有值以查看发生了什么变化。

            您可以通过将旧路由路径与shouldComponentUpdate 生命周期方法中的新路由路径进行比较来解决此问题。如果它没有改变,你就在同一条路线上,你可以通过返回 false 来阻止重新渲染。在所有其他情况下,请返回 true。默认情况下,这总是返回 true。

            shouldComponentUpdate: function(nextProps, nextState) {
              if(this.props.route.path == nextProps.route.path) return false;
              return true;
            }
            

            您必须进行进一步检查,这会阻止您的组件在组件内的state 更新上进行更新,但我想这将是您的起点。

            在官方react docs 页面上阅读有关shouldComponentUpdate 的更多信息。

            当您确定转换到新的道具和状态不需要更新组件时,可以利用这个机会返回 false。

            【讨论】:

            • 正确。对 props 的一瞥揭示了一系列特定于 react-router 和 history.push() 变化的属性。是的,action 和 key 在 shouldComponentUpdate 的调用中确实不同。赞成并接受。提议的解决方案确实有意义 - 但是,考虑到需要考虑的实际状态变化,我认为处理这个问题“在代码路径中为时已晚” - 我最终会做很多每个“顶级”组件中的冗余差异。也许我应该首先避免将这条路线推入历史..
            • 这不适用于 React router v4,它需要使用location.search
            • 此答案仅与基于 React 类的组件相关。对于功能组件,您可以使用 React.memo 包装它们以实现 shallow-props-comparing 不会重新渲染包装的组件,因为它不知道深层道具更改,除非 route 道具本身每次都是一个全新的对象...
            • 我正在使用功能组件。并尝试了 React.memo 但没有奏效。
            猜你喜欢
            • 2018-01-13
            • 2019-08-26
            • 2019-05-30
            • 2018-05-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-04-04
            • 1970-01-01
            相关资源
            最近更新 更多