【问题标题】:Programmatically Navigate using react-router使用 react-router 以编程方式导航
【发布时间】:2018-03-25 14:31:45
【问题描述】:

我正在开发一个应用程序,我在其中检查用户是否不是loggedIn。我必须显示登录表单,否则 dispatchaction 会更改路由并加载其他组件。这是我的代码:

render() {
    if (isLoggedIn) {
        // dispatch an action to change the route
    }
    // return login component
    <Login />
}

由于我无法更改 render 函数中的状态,我该如何实现这一点。

【问题讨论】:

标签: javascript reactjs redux react-router react-router-v4


【解决方案1】:

考虑到您正在使用react-router v4

将您的组件与withRouter 一起使用,并使用道具中的history.push 来更改路线。只有当你的组件没有接收到 Router 道具时,你才需要使用 withRouter,这可能发生在你的组件是由路由器渲染的组件的嵌套子级并且你没有传递路由器道具的情况下到它或当组件根本没有链接到路由器并呈现为与路由分开的组件时。

import {withRouter} from 'react-router';

class App extends React.Component {
     ...
     componenDidMount() {
        // get isLoggedIn from localStorage or API call
        if (isLoggedIn) {
             // dispatch an action to change the route
             this.props.history.push('/home');
        }
     }
     render() {
         // return login component
         return <Login />
    }
}


export default withRouter(App);

重要提示

如果您使用withRouter 来防止更新被 shouldComponentUpdate,重要的是 withRouter 将 实现shouldComponentUpdate 的组件。例如,当 使用 Redux:

// 这绕过shouldComponentUpdate

withRouter(connect(...)(MyComponent))

//这不是

connect(...)(withRouter(MyComponent))

或者你可以使用重定向

import {withRouter} from 'react-router';

class App extends React.Component {
     ...
     render() {
         if(isLoggedIn) {
              return <Redirect to="/home"/>
         }
         // return login component
         return <Login />
    }
}

有了react-router v2 or react-router v3,你可以利用context来动态改变路由

class App extends React.Component {
     ...
     render() {
         if (isLoggedIn) {
             // dispatch an action to change the route
             this.context.router.push('/home');
         }
         // return login component
         return <Login />
    }
}

App.contextTypes = {
    router: React.PropTypes.object.isRequired
}
export default App;

或使用

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

【讨论】:

  • 我正在使用 react-router v 2
  • @GauravMehta 考虑升级到 v3 或 4.. 会给你更多选择
  • 更新到 v2.0
  • 为什么这么复杂?我希望我们可以在任何地方history.pushState...
  • @ShubhamKhatri 不,不,谢谢,但我只是对设计选择感到好奇。为什么路由器不简单地监听状态变化?这样,链接就不会被绑定到 react-router (即简单的&lt;a/&gt; 元素就足够了)例如...
【解决方案2】:

在 react-router 版本 4 中:

import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'

const Example = () => (

  if (isLoggedIn) {
    <OtherComponent />

  } else {

    <Router>
      <Redirect push to="/login" />
      <Route path="/login" component={Login}/>
    </Router>

  }
)

const Login = () => (
    <h1>Form Components</h1>
    ...
)

export default Example;

【讨论】:

    【解决方案3】:

    另一种选择是使用 Thunk 样式的异步操作(安全/允许有副作用)来处理此问题。

    如果您使用 Thunk,您可以将相同的 history 对象注入到您的 &lt;Router&gt; 组件和使用 thunk.withExtraArgument 的 Thunk 操作中,如下所示:

    import React from 'react'
    import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
    import { createBrowserHistory } from "history"
    import { applyMiddleware, createStore } from "redux"
    import thunk from "redux-thunk"
    
    const history = createBrowserHistory()
    
    const middlewares = applyMiddleware(thunk.withExtraArgument({history}))
    const store = createStore(appReducer, middlewares)
    
    render(
      <Provider store={store}
        <Router history={history}>
          <Route path="*" component={CatchAll} />
        </Router
      </Provider>,
    appDiv)
    

    然后在你的 action-creators 中,你将有一个 history 实例,它可以安全地与 ReactRouter 一起使用,所以如果你没有登录,你可以触发一个常规的 Redux 事件:

    // meanwhile... in action-creators.js
    export const notLoggedIn = () => {
      return (dispatch, getState, {history}) => {
        history.push(`/login`)
      }
    }
    

    这样做的另一个好处是 url 现在更容易处理,所以我们可以将重定向信息放在查询字符串等上。

    您仍然可以尝试在您的 Render 方法中执行此检查,但如果它导致问题,您可以考虑在 componentDidMount 或生命周期的其他地方执行此检查(尽管我也理解坚持使用无状态功能组件的愿望! )

    您仍然可以使用 Redux 和 mapDispatchToProps 将动作创建者注入到您的组件中,因此您的组件仍然只是松散地连接到 Redux。

    【讨论】:

    • 好的,这看起来不错。但是CatchAll 是什么,我们真的需要 history 库吗?
    • 我已经有一段时间没有发布这个答案了,但我猜我是从一些生产代码中提取了这个例子并对其进行了精简。您的应用程序的Routes 将进入路由器内部。 CatchAll 这里只是一个例子,展示了Routes 的去向。 history 库是 already being used by React Router 如果您在历史模式下使用它。这种方法使我们能够从 actions 中以编程方式访问历史 API,而不是 withRouter,后者允许我们使用组件中的历史 API。
    【解决方案4】:
    render(){ 
    
        return (
            <div>  
                { this.props.redirect ? <Redirect to="/" /> :'' } 
                <div>
                    add here component codes
                </div>
            </div>
        );
    } 
    

    【讨论】:

      【解决方案5】:

      那些在 react-router v4 上实现此功能时遇到问题的人。这是一个以编程方式浏览 react 应用程序的有效解决方案。

      history.js

      import createHistory from 'history/createBrowserHistory'
      
      export default createHistory()
      

      App.js 或 Route.jsx。将历史记录作为道具传递给您的路由器。

      import { Router, Route } from 'react-router-dom'
      import history from './history'
      ...
      <Router history={history}>
       <Route path="/test" component={Test}/>
      </Router>
      

      您可以使用push() 进行导航。

      import history from './history' 
      
      ...
      
      render() {
           if (isLoggedIn) {
               history.push('/test') // this should change the url and re-render Test component
           }
           // return login component
           <Login />
      }
      

      感谢这条评论:https://github.com/ReactTraining/react-router/issues/3498#issuecomment-301057248

      【讨论】:

        【解决方案6】:

        我建议你使用 connected-react-router https://github.com/supasate/connected-react-router 如果您愿意,它甚至有助于从减速器/动作执行导航。 它有据可查且易于配置

        【讨论】:

          【解决方案7】:

          这是我的句柄loggedInreact-router v4

          PrivateRoute 允许在用户登录时输入 path 并将令牌保存到 localStorge

              function PrivateRoute({ component: Component, ...rest }) {
               return (
                  <Route
                    {...rest}
                    render={props => (localStorage.token) ? <Component {...props} /> : (
                      <Redirect
                        to={{
                          pathname: '/signin',
                          state: { from: props.location },
                        }}
                      />
                    )
                    }
                  />
                );
              }
          

          在此处定义应用中的所有路径

          export default (
            <main>
              <Switch>
                <Route exact path="/signin" component={SignIn} />
                <Route exact path="/signup" component={SignUp} />
                <PrivateRoute path="/" component={Home} />
              </Switch>
            </main>
          );
          

          【讨论】:

          • add对你的回答做一些解释。
          【解决方案8】:

          我能够在无状态功能组件中使用history,使用 withRouter 以下方式(需要忽略打字稿警告):

          import { withRouter } from 'react-router-dom';
          
          ...
          
          type Props = { myProp: boolean };
          
          // @ts-ignore
          export const MyComponent: FC<Props> = withRouter(({ myProp, history }) => {
          
          ...
          
          })
          

          【讨论】:

            猜你喜欢
            • 2017-10-23
            • 2017-07-31
            • 2018-04-04
            • 2019-09-11
            • 2016-08-10
            相关资源
            最近更新 更多