【问题标题】:How do I programaticly navigate to a route from an event handler in React Router 4.0?如何以编程方式从 React Router 4.0 中的事件处理程序导航到路由?
【发布时间】:2020-11-28 12:54:07
【问题描述】:

我需要在活动成功后导航到路线。 这似乎比以前的版本有所改变。 以前我们会这样做:

import { browserHistory } from 'react-router';
...
handleClick(){
  doSomething();
  browserHistory.push('/some/path');
}

【问题讨论】:

    标签: javascript reactjs router


    【解决方案1】:

    此示例适用于 react-router 和 react-router-dom v4.0.0

    我们有 3 个组件:

    • App.js - 包含组件 1 和组件 2
    • Component1.js - 未包装在 Route 中并且将始终被渲染,但这不会有“路由道具”的引用 -(历史、位置、匹配...)
    • Component2.js - 仅在路由位置匹配时才呈现。需要注意的重要一点是,该组件将使用“路由道具”进行渲染

    要以编程方式导航,您可以使用 react-router history 对象。

    this.props.history.push('path');

    对于通过 Route 渲染的组件,这将立即生效,因为这些组件已经可以访问 route props(历史记录)。在我们的示例中,这是 Component2。但是,对于不通过 Route 渲染的组件(例如 Component1),您需要将其包装在 withRouter 中,以便您可以访问 history 对象。


    App.js

    import React, { Component } from 'react';
    import { BrowserRouter, Route } from 'react-router-dom';
    
    import Component1 from './Component1';
    import Component2 from './Component2';
    
    class App extends Component {
      render() {
        return (
          <BrowserRouter>
            <div>
              <Component1 />
              <Route path="/render1" render={() => <div>Rendered from Component1</div>} />
              <Route path="/" component={Component2} />
              <Route path="/render2" render={() => <div>Rendered from Component2</div>} />
            </div>
          </BrowserRouter>
        )
      }
    }
    
    export default App;
    

    Component1.js

    import React from 'react';
    import { withRouter } from 'react-router';
    
    class Component1 extends React.Component {
    
      handleButtonClick() {
        this.props.history.push('/render1');
      }
    
      render() {
        return (
          <div>
            <h1>Component 1</h1>
            <button onClick={this.handleButtonClick.bind(this)}>Component 1 Button</button>
          </div>
        );
      }
    }
    
    const Component1WithRouter = withRouter(Component1);
    export default Component1WithRouter;
    

    对于Component1,我们将其包裹在withRouter中,然后将返回的包裹对象导出。一些问题,请注意在 App.js 中,我们仍然将其引用为 Component1 而不是 Component1WithRouter


    Component2.js

    import React from 'react';
    
    class Component2 extends React.Component {
    
      handleButtonClick() {
        this.props.history.push('/render2');
      }
    
      render() {
        return (
          <div>
            <h1>Component 2</h1>
            <button onClick={this.handleButtonClick.bind(this)}>Component 2 Button</button>
          </div>
        );
      }
    }
    
    export default Component2;
    

    对于 Component2,历史对象已经可以从 this.props 获得。你只需要调用 push 函数。

    【讨论】:

    • 谢谢,你的例子比我贴的更完整。
    • 当我尝试使用 Component2 时,控制台中出现错误“TypeError: Cannot read property 'push' of undefined”。
    【解决方案2】:

    如果您使用“函数组件”和 Hooks,而不是期望 props,请改用 useHistory() 函数:

    import {useHistory} from 'react-router-dom';
    
    export default function MyFunctionComponent() {
        const history = useHistory();
    
        const myEventHandler = () => {
            // do stuff
            history.push('/newpage');
        };
    
        // ...
    }
    

    【讨论】:

      【解决方案3】:

      只需渲染一个redirect 组件:

      import { Redirect } from 'react-router';
      // ...
      <Redirect to="/some/path" />
      

      在此处查看文档:https://reacttraining.com/react-router/core/api/Redirect

      【讨论】:

      • 我不确定如何从事件处理程序中执行此操作。我尝试在我的事件处理程序中设置:this.setState('successful',true);,然后在我的渲染函数中设置if(this.state.successful) return (&lt;Redirect to="/dogs"/&gt;);,但现在我在事件处理程序中得到“不变违规”。
      • 你的意思是this.setState({ successful: true })我猜
      • 是的,我想这样就可以了?
      • 如果您对我提出的另一个问题提出此答案,我会将其标记为正确:stackoverflow.com/questions/43011694/…
      【解决方案4】:

      我非常感谢 CodinCat 的回答,因为他帮助我解决了另一个错误,但我找到了更正确的解决方案:

      在 React Router 4.0(我不知道以前的版本)中,路由器将历史对象传递给组件(即:this.props.history)。您可以将您的 url 推送到该数组以重定向:

      this.props.history.push('/dogs');
      

      在我的例子中,我有两个级别的组件,路由器称为一个名为 LoginPage 的组件,而 LoginPage 称为一个名为 Login 的组件。如果您将其传递给您的子对象,您只会在它的 props 中拥有历史对象:

      <Router>
        <Route path="/dogs" component={DogsPage}/>
        <Route path="/login" component={LoginPage}/>
      </Router>
      
      const LoginPage = (props) => {
        // Here I have access to props.history
        return (
          <div>
            <Login history={props.history} />
          </div>
        )
      }
      
      const Login = (props) => {
        function handleClick(){
          // Now we can simply push our url onto the history and the browser will update
          props.history.push('/dogs');
        }
        return (
          <div>
            <button onClick={handleClick}>Click to Navigate</button>
          </div>
        )
      }
      

      我知道上面的示例有点做作,因为在这种情况下使用链接会更容易,但我以这种方式制作示例以保持简洁。您可能需要以这种方式导航的原因有很多。就我而言,我正在执行一个 graphQL 请求,并希望在有人成功登录后导航到主页。

      【讨论】:

        猜你喜欢
        • 2018-03-25
        • 2017-10-23
        • 2018-09-26
        相关资源
        最近更新 更多