使用 react-router 以编程方式导航

IT技术 javascript reactjs redux react-router react-router-v4
2021-01-15 08:32:54

我正在开发一个应用程序,我在其中检查用户是否不是loggedIn. 我有显示登录表单,否则dispatch一个action将改变路线和加载其他组件。这是我的代码:

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

我如何才能实现这一点,因为我无法更改render函数内部的状态

6个回答

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

使用你的组件withRouterhistory.pushfrom props来改变路线。您只需要withRouter在您的组件未接收到Routerprops时才需要使用根本没有链接到路由器,而是作为独立于路由的组件呈现。

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');
@GauravMehta 考虑升级到 v3 或 4 .. 会给你更多选择
2021-03-10 08:32:54
为什么这么复杂?我希望我们可以history.pushState从任何地方...
2021-03-13 08:32:54
我正在使用 react-router v 2
2021-03-20 08:32:54
针对 v2.0 更新
2021-03-25 08:32:54
@ShubhamKhatri 不,不,谢谢,但我只是对设计选择感到好奇。为什么路由器不简单地监听状态变化?这样,链接就不会绑定到 react-router (即简单的<a/>元素就足够了),例如......
2021-04-07 08:32:54

在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;

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

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

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。

我发布这个答案已经有一段时间了,但我猜我从一些生产代码中提取了这个例子并将其精简。您的应用程序Route将进入路由器。CatchAll这里只是一个例子来展示Routes 的去向。如果您在历史模式下使用history库,则React Router 已经在使用它。这种方法使我们能够从我们的操作中以编程方式访问历史 API ,而不是withRouter让我们从组件中使用历史 API。
2021-03-20 08:32:54
好的,这看起来不错。但是CatchAll我们真的需要历史图书馆吗?
2021-04-08 08:32:54

这是我的把手loggedInreact-router v4

path如果用户已登录,则允许进入 PrivateRoute并将令牌保存到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>
);
在您的答案中添加一些解释。
2021-03-11 08:32:54

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

历史.js

import createHistory from 'history/createBrowserHistory'

export default createHistory()

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

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