如何使用react-router重定向到另一个路由?

186

我正在尝试使用react-router(版本^1.0.3)做一件简单的事情,即重定向到另一个视图。

import React from 'react';
import {Router, Route, Link, RouteHandler} from 'react-router';


class HomeSection extends React.Component {

  static contextTypes = {
    router: PropTypes.func.isRequired
  };

  constructor(props, context) {
    super(props, context);
  }

  handleClick = () => {
    console.log('HERE!', this.contextTypes);
    // this.context.location.transitionTo('login');
  };

  render() {
    return (
      <Grid>
        <Row className="text-center">          
          <Col md={12} xs={12}>
            <div className="input-group">
              <span className="input-group-btn">
                <button onClick={this.handleClick} type="button">
                </button>
              </span>
            </div>
          </Col>
        </Row>
      </Grid>
    );
  }
};

HomeSection.contextTypes = {
  location() {
    React.PropTypes.func.isRequired
  }
}

export default HomeSection;

我只需要将用户重定向到'/login',就可以了。

我能做什么?

控制台显示以下错误:

Uncaught ReferenceError: PropTypes is not defined

我的路由文件

// LIBRARY
/*eslint-disable no-unused-vars*/
import React from 'react';
/*eslint-enable no-unused-vars*/
import {Route, IndexRoute} from 'react-router';

// COMPONENT
import Application from './components/App/App';
import Contact from './components/ContactSection/Contact';
import HomeSection from './components/HomeSection/HomeSection';
import NotFoundSection from './components/NotFoundSection/NotFoundSection';
import TodoSection from './components/TodoSection/TodoSection';
import LoginForm from './components/LoginForm/LoginForm';
import SignupForm from './components/SignupForm/SignupForm';

export default (
    <Route component={Application} path='/'>
      <IndexRoute component={HomeSection} />
      <Route component={HomeSection} path='home' />
      <Route component={TodoSection} path='todo' />
      <Route component={Contact} path='contact' />
      <Route component={LoginForm} path='login' />
      <Route component={SignupForm} path='signup' />
      <Route component={NotFoundSection} path='*' />
    </Route>
);

而不是按钮,使用<Link to="/login">登录</Link> - aarosil
你使用的是哪个版本的react-router?在不同的主要版本中,程序重定向的代码已经发生了变化。 - mjhm
4
对于"Uncaught ReferenceError",你正在使用"PropTypes"来调用它,但是你没有导入它,你需要将"PropTypes"本身导入或使用"React.PropTypes"。 - aarosil
react-router 具有使用 this.context.router.push('/login') 推送路径的能力。这是您正在寻找的吗?另外,您的 ReferenceError 是因为您没有从任何地方导入 PropTypes。您需要使用 React.PropTypes - Josh David Miller
1
@JoshDavidMiller 说得好,不过我不会惊讶如果 react-router 的 API 在5分钟内发生变化.. 哈哈开玩笑,只是部分如此。 - aarosil
显示剩余5条评论
11个回答

302

1) react-router > V6 useNavigate 钩子:

如果你使用的是 React >= 16.8 和函数式组件,可以使用来自react-router的useNavigate hook进行导航。

import React from 'react';
import { useNavigate } from "react-router-dom";

const YourComponent = () => {
    const navigate = useNavigate();

    const handleClick = () => {
        navigate("/path/to/push");
    }

    return (
        <div>
            <button onClick={handleClick} type="button" />
        </div>
    );
}

export default YourComponent;

2) react-router > V5 useHistory hook:

如果您使用的是react-router v5和函数式组件,您可以使用react-router中的useHistory hook

import React from 'react';
import { useHistory } from 'react-router-dom';

const YourComponent = () => {
    const history = useHistory();

    const handleClick = () => {
        history.push("/path/to/push");
    }

    return (
        <div>
            <button onClick={handleClick} type="button" />
        </div>
    );
}

export default YourComponent;

3) react-router > V4 withRouter HOC:

正如评论中@ambar所提到的,自从React-Router V4以后,它的代码库已经发生了变化。这里是withRouter的文档。

import React, { Component } from 'react';
import { withRouter } from "react-router-dom";

class YourComponent extends Component {
    handleClick = () => {
        this.props.history.push("path/to/push");
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick} type="button">
            </div>
        );
    };
}

export default withRouter(YourComponent);

4) 使用browserHistory的React-router版本<V4

您可以使用React-router的BrowserHistory来实现此功能。以下是代码:

import React, { Component } from 'react';
import { browserHistory } from 'react-router-dom';

export default class YourComponent extends Component {
    handleClick = () => {
        browserHistory.push('/login');
    };

    render() {
        return (
            <div>
                <button onClick={this.handleClick} type="button">
            </div>
        );
    };
}

5) Redux connected-react-router

如果您已经将组件与Redux连接,并配置了connected-react-router,那么您所需做的就是this.props.history.push("/new/url"); 即,您不需要使用withRouter HOC 将history注入到组件属性中。

// reducers.js
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';

export default (history) => combineReducers({
    router: connectRouter(history),
    ... // rest of your reducers
});


// configureStore.js
import { createBrowserHistory } from 'history';
import { applyMiddleware, compose, createStore } from 'redux';
import { routerMiddleware } from 'connected-react-router';
import createRootReducer from './reducers';
...
export const history = createBrowserHistory();

export default function configureStore(preloadedState) {
    const store = createStore(
        createRootReducer(history), // root reducer with router state
        preloadedState,
        compose(
            applyMiddleware(
                routerMiddleware(history), // for dispatching history actions
                // ... other middlewares ...
            ),
        ),
    );

    return store;
}


// set up other redux requirements like for eg. in index.js
import { Provider } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router';
import configureStore, { history } from './configureStore';
...
const store = configureStore(/* provide initial state if any */)

ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <> { /* your usual react-router v4/v5 routing */ }
                <Switch>
                    <Route exact path="/yourPath" component={YourComponent} />
                </Switch>
            </>
        </ConnectedRouter>
    </Provider>,
    document.getElementById('root')
);


// YourComponent.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
...

class YourComponent extends Component {
    handleClick = () => {
        this.props.history.push("path/to/push");
    }

    render() {
        return (
          <div>
            <button onClick={this.handleClick} type="button">
          </div>
        );
      }
    };

}

export default connect(mapStateToProps = {}, mapDispatchToProps = {})(YourComponent);

6
貌似“browserHistory”不再是react-router的一部分了。 - ambar
BrowserRouter没有push函数。 - johnnyodonnell
1
这是最佳答案。您应该将此答案标记为正确答案。 - Solomon Bush
UseHistory接受路径字符串,但要求必须正确设置路由器。更多信息请参见此处。https://reactrouter.com/web/api/Hooks/usehistory - Noushad
1
它每个版本都有如此大的变化,这清楚地表明开发人员没有提前规划的能力。感谢您的努力。 - Coreus
显示剩余2条评论

49

简单来说,你可以使用react-router中的Link组件,而不是button。虽然有JS的方法可以改变路由,但似乎在这里不需要。

<span className="input-group-btn">
  <Link to="/login">Click to login</Link>
</span>

如果您想以编程的方式在1.0.x中实现这一点,您可以在clickHandler函数中像这样做:

this.history.pushState(null, 'login');

取自升级文档此处

您应该将this.history放置在由react-router处理路由的组件中。如果它是在routes定义中提到的那个组件的子组件,则可能需要进一步向下传递。


3
这是个不错的解决方案,但我没有使用它的原因是因为我需要先做一种验证。我需要把这个放在一个函数里面,例如:if (true) { // 跳转到登录页 } 所以这就是为什么我把它放在了一个onClick函数里面的原因。 - Reacting
5
在JSX中,您也可以这样做:{validation && <Link to="/login">点击登录</Link>}。如果验证为假,则不会呈现任何内容。 - aarosil
我知道你的意思,但如果验证成功,我需要按钮在那里,然后重定向,否则应该出现错误消息。 - Reacting
1
未捕获的类型错误:无法读取未定义的“pushState”属性。 - Reacting
1
这段代码里是不是有个错别字?<Link to="/login" /> 是一个自闭合的链接标签。 - Aimal Khan
显示剩余4条评论

32
如何使用react-router进行路由重定向?
例如,当用户点击链接 <Link to="/" />Click to route</Link> 时,react-router将查找/并且您可以使用Redirect to将用户重定向到其他路由,如登录路由。
来自ReactRouterTraining文档
渲染<Redirect>将导航到新位置。 新位置将覆盖历史堆栈中的当前位置,就像服务器端重定向(HTTP 3xx)一样。
import { Route, Redirect } from 'react-router'

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>

to: 字符串,需要重定向到的URL。

<Redirect to="/somewhere/else"/>

to: object, A location to redirect to.

目标:对象,重定向到的位置。
<Redirect to={{
  pathname: '/login',
  search: '?utm=your+face',
  state: { referrer: currentLocation }
}}/>

提供的解决方案会抛出错误"<Redirect>元素仅用于路由配置,不应呈现"。 - t1gor
1
变得更糟的是:“在'react-router'中未找到'Redirect'导出”这些路由器包到底怎么了?我需要检查和比较每个应用程序中的每个版本,并为每个版本使用不同的方法吗?这简直是一场灾难性的React。 - Rin and Len

21

最简单的网页解决方案!

截至2020年最新版
已确认可用:

"react-router-dom": "^5.1.2"
"react": "^16.10.2"

使用useHistory()钩子!

import React from 'react';
import { useHistory } from "react-router-dom";


export function HomeSection() {
  const history = useHistory();
  const goLogin = () => history.push('login');

  return (
    <Grid>
      <Row className="text-center">          
        <Col md={12} xs={12}>
          <div className="input-group">
            <span className="input-group-btn">
              <button onClick={goLogin} type="button" />
            </span>
          </div>
        </Col>
      </Row>
    </Grid>
  );
}

1
太好了,我正在寻找实现它的钩子方式!尽管我的IDE显示“无法解析符号…”警告,但它确实有效! - Rafael Moni
1
太好了,这正是我在寻找的,并且它在这里起作用。 - nanquim
@scottplunket,你在哪里将“login”与Login组件进行映射? - Kartik Dolas

17

我知道这是一个老问题,但对于在2021年及之后来到这里的人来说,React Router V6已不再从react-router-dom中导出useHistory,而是需要导入useNavigate。以下是示例代码:

import { useNavigate } from "react-router-dom"

并且在你的 React 类或函数组件内部:

const navigate = useNavigate()
navigate("/404")

10

如果你正在使用react-router v2.8.1(可能也适用于其他2.x.x版本,但我没有测试过),你可以使用这个实现来进行路由重定向。

import { Router } from 'react-router';

export default class Foo extends Component {

  static get contextTypes() {
    return {
      router: React.PropTypes.object.isRequired,
    };
  }

  handleClick() {
    this.context.router.push('/some-path');
  }
}

有时候:this.context.router.history.push('/some-path'); - 象嘉道

9
最简单的解决方案是:
import { Redirect } from 'react-router';

<Redirect to='/componentURL' />

1
但是我遇到了错误:不变式失败:您不应在<Router>外部使用<Redirect>。 - Prateek Gupta
你尝试过包装吗? - Jackkobec

2

更新 react-router-dom 版本 >= 6

useHistory 已被 useNavigate 替换。

你可以按如下方式使用 useNavigate

import {useNavigate} from 'react-router-dom';
const navigate = useNavigate();
navigate('/login');

1

不使用任何路由库的最简单方法

window.location.replace("/login")

0

React-Router 解决方案

为什么选择此解决方案: 组件加载更快,路由(例如www.website.com/**signup)和链接到它们的组件可以在一个位置轻松管理

如何实现:您可以在 index.js 中设置路由,例如www.website.com/**home,然后在其他组件中使用超链接来转到这些路由。

步骤 1:安装 react-router-dom。


npm install react-router-dom

第二步:在./src中创建一个启动和注册组件。


Start.jsx

import React from 'react';
import { Link } from 'react-router-dom';

class Start extends React.Component {
    constructor(props) {
        super(props);
    }

    render () {
        return (
            <div>
               <h1>Hello World!</h1>
               <Link to='signUp'>Sign Up</Link>
           </div>
        );
    }
};

export default Start

SignUp.jsx

import React from "react";

class SignUp extends React.Component {
    constructor(props) {
        super(props);
    }
    
    render() {
        return (
        <div>
            <h1>Sign Up</h1>
            <p>Sign up page</p>
        </div>
        );
    }
    }

export default SignUp;

步骤三:使用以下代码更新您的index.js和App.js文件。


index.js

//Imports
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider} from 'react-router-dom';

import SignUp from './SignUp';
import Start from './Start';

/*This is where you create the routes - path is the 
route and element is the component that will be found there. */

const router = createBrowserRouter([
  {
      path: '/',
      element: <Start />
  },
  {
      path: 'signUp',
      element: <SignUp />
  }
]);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
        <RouterProvider router = { router } />
  </React.StrictMode>
);

App.js

import React from "react";
import Start from "./Start";

function App() {
  return (
    <div>
      <Start />
    </div>
  );
}

export default App;

完成了。不再只有函数组件,只有导航,只有即时客户端路由和简单的路由管理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接