使用 React 路由器以编程方式导航

Programmatically navigate using React router

使用 react-router 我可以使用 Link 元素来创建 links,它们由 React 路由器本地处理。

我看到它在内部调用 this.context.transitionTo(...)

我想做导航。不是来自 link,而是来自下拉选择(作为示例)。我怎样才能在代码中做到这一点?什么是 this.context

我看到了 Navigation mixin,但是我可以在没有 mixins 的情况下这样做吗?

React-Router v6+ Answer

您可以使用新的 useNavigate 挂钩。 useNavigate hook returns 一个可用于编程导航的函数。 来自 React 路由器的示例 documentaion

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

function SignupForm() {
  let navigate = useNavigate();

  async function handleSubmit(event) {
    event.preventDefault();
    await submitForm(event.target);
    navigate("../success", { replace: true });
  }

  return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}

React-Router 5.1.0+ Answer (using hooks and React >16.8)

您可以在功能组件上使用 useHistory 挂钩并以编程方式导航:

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

function HomeButton() {
  let history = useHistory();
  // use history.push('/some/path') here
};

React-Router 4.0.0+ Answer

在 4.0 及更高版本中,将历史记录用作组件的 prop。

class Example extends React.Component {
   // use `this.props.history.push('/some/path')` here
};

注意:如果您的组件不是由 <Route> 呈现的,则 this.props.history 不存在。您应该使用 <Route path="..." component={YourComponent}/> 在 YourComponent

中包含 this.props.history

React-Router 3.0.0+ Answer

在 3.0 及更高版本中,将路由器用作组件的 prop。

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

React-Router 2.4.0+ Answer

在 2.4 及更高版本中,使用高阶组件将路由器作为组件的 prop。

import { withRouter } from 'react-router';

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

// Export the decorated class
var DecoratedExample = withRouter(Example);

// PropTypes
Example.propTypes = {
  router: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  }).isRequired
};

React-Router 2.0.0+ Answer

此版本向后兼容 1.x,因此无需升级指南。只需浏览示例就足够了。

也就是说,如果您想切换到新模式,您可以使用路由器中的 browserHistory 模块访问

import { browserHistory } from 'react-router'

现在您可以访问您的浏览器历史记录,因此您可以执行推送、替换等操作...喜欢:

browserHistory.push('/some/path')

进一步阅读: HistoriesNavigation


React-Router 1.x.x Answer

升级的细节就不说了。您可以在 Upgrade Guide

中阅读相关内容

此处问题的主要变化是从 Navigation mixin 更改为 History。现在它使用浏览器 historyAPI 来更改路由,所以我们从现在开始将使用 pushState()

下面是一个使用 Mixin 的例子:

var Example = React.createClass({
  mixins: [ History ],
  navigateToHelpPage () {
    this.history.pushState(null, `/help`);
  }
})

请注意,此 History 来自 rackt/history 项目。不是来自 React-Router 本身。

如果你出于某种原因不想使用 Mixin(可能是因为 ES6 class),那么你可以从 this.props.history 访问你从路由器获得的历史记录。只有您的路由器呈现的组件才能访问它。所以,如果你想在任何子组件中使用它,它需要通过 props.

作为属性传递下去

您可以在 1.0.x documentation

阅读更多关于新版本的信息

这里是a help page specifically about navigating outside your component

它建议抓取参考 history = createHistory() 并在其上调用 replaceState

React-Router 0.13.x Answer

我遇到了同样的问题,只能通过 react-router 自带的 Navigation mixin 找到解决方案。

这是我的做法

import React from 'react';
import {Navigation} from 'react-router';

let Authentication = React.createClass({
  mixins: [Navigation],

  handleClick(e) {
    e.preventDefault();

    this.transitionTo('/');
  },

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

我能够调用 transitionTo() 而无需访问 .context

或者你可以试试花哨的 ES6 class

import React from 'react';

export default class Authentication extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    e.preventDefault();

    this.context.router.transitionTo('/');
  }

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
}

Authentication.contextTypes = {
  router: React.PropTypes.func.isRequired
};

React-Router-Redux

Note: if you're using Redux, there is another project called React-Router-Redux that gives you redux bindings for ReactRouter, using somewhat the same approach that React-Redux does

React-Router-Redux 有一些可用的方法,允许从内部操作创建者进行简单的导航。这些对于在 React Native 中拥有现有架构的人特别有用,他们希望以最小的样板开销在 React Web 中使用相同的模式。

探索以下方法:

  • push(location)
  • replace(location)
  • go(number)
  • goBack()
  • goForward()

这是一个用法示例,Redux-Thunk:

./actioncreators.js

import { goBack } from 'react-router-redux'

export const onBackPress = () => (dispatch) => dispatch(goBack())

./viewcomponent.js

<button
  disabled={submitting}
  className="cancel_button"
  onClick={(e) => {
    e.preventDefault()
    this.props.onBackPress()
  }}
>
  CANCEL
</button>

Warning: this answer covers only ReactRouter versions before 1.0

I will update this answer with 1.0.0-rc1 use cases after!

你也可以不使用 mixin 来做到这一点。

let Authentication = React.createClass({
  contextTypes: {
    router: React.PropTypes.func
  },
  handleClick(e) {
    e.preventDefault();
    this.context.router.transitionTo('/');
  },
  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

上下文的问题在于,除非您在 class 上定义 contextTypes,否则无法访问它。

至于什么是context,就是一个对象,和props一样,从parent传给child,但是是隐式传递的,不用每次都重新声明props。参见 https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html

React-Router v2

对于最新版本 (v2.0.0-rc5),推荐的导航方法是直接推送到历史单例。您可以在 Navigating outside of Components doc 中看到它的实际效果。

相关摘录:

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

如果使用较新的 react-router API,您需要在组件内部使用 this.props 中的 history,因此:

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

它还提供 pushState 但根据记录的警告已弃用。

如果使用 react-router-redux,它提供了一个 push 函数,您可以像这样调度:

import { push } from 'react-router-redux';
this.props.dispatch(push('/some/path'));

然而,这可能仅用于更改 URL,而不是实际导航到页面。

以下是使用 react-router v2.0.0 with ES6 执行此操作的方法。 react-router 已经远离 mixins。

import React from 'react';

export default class MyComponent extends React.Component {
  navigateToPage = () => {
    this.context.router.push('/my-route')
  };

  render() {
    return (
      <button onClick={this.navigateToPage}>Go!</button>
    );
  }
}

MyComponent.contextTypes = {
  router: React.PropTypes.object.isRequired
}

对于这个,谁不控制服务器端,因此使用散列路由器 v2:

将您的 history 放入单独的文件中(例如 app_history.js ES6):

import { useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });

export default appHistory;

随处使用!

你的反应路由器入口点(app.js ES6):

import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Redirect } from 'react-router'
import appHistory from './app_history'
...
const render((
  <Router history={appHistory}>
  ...
  </Router>
), document.querySelector('[data-role="app"]'));

您在任何组件 (ES6) 中的导航:

import appHistory from '../app_history'
...
ajaxLogin('/login', (err, data) => {
  if (err) {
    console.error(err); // login failed
  } else {
    // logged in
    appHistory.replace('/dashboard'); // or .push() if you don't need .replace()
  }
})

对于 ES6 + React 组件,以下解决方案对我有用。

我关注了 Felippe skinner,但添加了一个端到端的解决方案来帮助像我这样的初学者。

以下是我使用的版本:

"react-router": "^2.7.0"

"react": "^15.3.1"

下面是我的反应组件,我在其中使用反应路由器进行编程导航:

import React from 'react';

class loginComp extends React.Component {
   constructor( context) {
    super(context);
    this.state = {
      uname: '',
      pwd: ''
    };
  }

  redirectToMainPage(){
        this.context.router.replace('/home');
  }

  render(){
    return <div>
           // skipping html code 
             <button onClick={this.redirectToMainPage.bind(this)}>Redirect</button>
    </div>;
  }
};

 loginComp.contextTypes = {
    router: React.PropTypes.object.isRequired
 }

 module.exports = loginComp;

下面是我的路由器的配置:

 import { Router, Route, IndexRedirect, browserHistory } from 'react-router'

 render(<Router history={browserHistory}>
          <Route path='/' component={ParentComp}>
            <IndexRedirect to = "/login"/>
            <Route path='/login' component={LoginComp}/>
            <Route path='/home' component={HomeComp}/>
            <Route path='/repair' component={RepairJobComp} />
            <Route path='/service' component={ServiceJobComp} />
          </Route>
        </Router>, document.getElementById('root'));

随着 React-Router v4 的出现,现在有一种新的方法可以做到这一点。

import { MemoryRouter, BrowserRouter } from 'react-router';

const navigator = global && global.navigator && global.navigator.userAgent;
const hasWindow = typeof window !== 'undefined';
const isBrowser = typeof navigator !== 'undefined' && navigator.indexOf('Node.js') === -1;
const Router = isBrowser ? BrowserRouter : MemoryRouter;

<Router location="/page-to-go-to"/>

react-lego is an example app that shows how to use/update react-router 并且它包含导航应用程序的示例功能测试。

这可能不是最好的方法,但是...使用 react-router v4,以下 TypeScript 代码可以为某些人提供一个想法。

在下面的渲染组件中,例如LoginPagerouter 对象可访问,只需调用 router.transitionTo('/homepage') 即可导航。

导航码被占用from

"react-router": "^4.0.0-2", "react": "^15.3.1",

import Router from 'react-router/BrowserRouter';
import { History } from 'react-history/BrowserHistory';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();

interface MatchWithPropsInterface {
  component: typeof React.Component,
  router: Router,
  history: History,
  exactly?: any,
  pattern: string
}

class MatchWithProps extends React.Component<MatchWithPropsInterface,any> {
  render() {
    return(
      <Match {...this.props} render={(matchProps) => (
             React.createElement(this.props.component, this.props)

        )}
       />
    )
  }
}

ReactDOM.render(
    <Router>
      {({ router }) => (
        <div>
          <MatchWithProps exactly pattern="/" component={LoginPage} router={router} history={history} />
          <MatchWithProps pattern="/login" component={LoginPage} router={router} history={history} />
          <MatchWithProps pattern="/homepage" component={HomePage} router={router} history={history} />
          <Miss component={NotFoundView} />
        </div>
      )}
    </Router>,

   document.getElementById('app')
);

对于当前的 React 版本 (15.3),this.props.history.push('/location'); 对我有用,但它显示了以下警告:

browser.js:49 Warning: [react-router] props.history and context.history are deprecated. Please use context.router.

我用 context.router 解决了这个问题:

import React from 'react';

class MyComponent extends React.Component {

    constructor(props) {
        super(props);
        this.backPressed = this.backPressed.bind(this);
    }

    backPressed() {
        this.context.router.push('/back-location');
    }

    ...
}

MyComponent.contextTypes = {
    router: React.PropTypes.object.isRequired
};

export default MyComponent;

React-Router V4

如果您使用的是版本 4,那么您可以使用我的库(shameless 插件),您只需发送一个动作,一切正常!

dispatch(navigateTo("/aboutUs"));

trippler

这是最简单和最干净的方法,大约当前的 React-Router 3.0.0 和 ES6:

React-Router 3.x.x with ES6:

import { withRouter } from 'react-router';

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

// Export the decorated class
export default withRouter(Example);

或者,如果它不是您的默认设置 class,则导出为:

withRouter(Example);
export { Example };

请注意,在 3.x.x 中,<Link> 组件本身正在使用 router.push,因此您可以将任何要传递给 <Link to= 标记的内容传递给它,例如:

   this.props.router.push({pathname: '/some/path', query: {key1: 'val1', key2: 'val2'})'

基于之前的

新奇?将用 TypeScript 编写并使用 decorators 一个静态property/field

import * as React from "react";
import Component = React.Component;
import { withRouter } from "react-router";

export interface INavigatorProps {
    router?: ReactRouter.History.History;
}

/**
 * Note: goes great with mobx
 * @inject("something") @withRouter @observer
 */
@withRouter
export class Navigator extends Component<INavigatorProps, {}>{
    navigate: (to: string) => void;
    constructor(props: INavigatorProps) {
        super(props);
        let self = this;
        this.navigate = (to) => self.props.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

/**
 * Non decorated
 */
export class Navigator2 extends Component<INavigatorProps, {}> {

    static contextTypes = {
        router: React.PropTypes.object.isRequired,
    };

    navigate: (to: string) => void;
    constructor(props: INavigatorProps, context: any) {
        super(props, context);
        let s = this;
        this.navigate = (to) =>
            s.context.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

今天安装的任何 npm。

“反应路由器”:“^3.0.0”和
"@types/react-router": "^2.0.41"

React Router v5.1.0 with hooks

如果您使用 React >16.8.0 和功能组件,React Router >5.1.0 中有一个新的 useHistory 挂钩。

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

function HomeButton() {
  const history = useHistory();

  function handleClick() {
    history.push("/home");
  }

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

React Router v4

使用 React Router v4,您可以采用三种方法在组件内进行编程路由。

  1. 使用 withRouter higher-order 组件。
  2. 使用合成并渲染 <Route>
  3. 使用 context.

React Router 主要是 history library. history handles interaction with the browser's window.history 的包装器,带有浏览器和哈希历史记录。它还提供了一个内存历史,这对于没有全局历史的环境很有用。这在移动应用程序开发 (react-native) 和 Node 单元测试中特别有用。

一个history实例有两种导航方法:pushreplace。如果您将 history 视为访问位置的数组,push 将向数组添加一个新位置,而 replace 将用新位置替换数组中的当前位置。通常,您会希望在导航时使用 push 方法。

在早期版本的 React Router 中,您必须创建自己的 history 实例,但在 v4 中,<BrowserRouter><HashRouter><MemoryRouter> 组件将创建为您提供浏览器、哈希和内存实例。 React Router 使与你的路由器关联的 history 实例的属性和方法可通过 router 对象下的上下文获得。

1。使用 withRouter higher-order 组件

withRouter higher-order 组件将注入 history 对象作为组件的 prop。这允许您访问 pushreplace 方法而无需处理 context.

import { withRouter } from 'react-router-dom'
// this also works with react-router-native

const Button = withRouter(({ history }) => (
  <button
    type='button'
    onClick={() => { history.push('/new-location') }}
  >
    Click Me!
  </button>
))

2。使用合成并渲染 <Route>

<Route> 组件不仅仅用于匹配位置。您可以渲染一条无路径路线,它将始终匹配当前位置<Route> 组件传递与 withRouter 相同的属性,因此您将能够通过 history 属性访问 history 方法。

import { Route } from 'react-router-dom'

const Button = () => (
  <Route render={({ history}) => (
    <button
      type='button'
      onClick={() => { history.push('/new-location') }}
    >
      Click Me!
    </button>
  )} />
)

3。使用上下文*

但你可能不应该

最后一个选项只有在您对 React 的 context 模型感到满意时才应该使用(React 的上下文 API 从 v16 开始稳定)。

const Button = (props, context) => (
  <button
    type='button'
    onClick={() => {
      // context.history.push === history.push
      context.history.push('/new-location')
    }}
  >
    Click Me!
  </button>
)

// you need to specify the context type so that it
// is available within the component
Button.contextTypes = {
  history: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  })
}

1 和 2 是最容易实施的选择,因此对于大多数用例而言,它们是您的最佳选择。

要以编程方式进行导航,您需要将新的 历史记录 推送到 [=11] 中的 props.history =],所以像这样的东西可以为你完成工作:

//using ES6
import React from 'react';

class App extends React.Component {

  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick(e) {
    e.preventDefault()
    /* Look at here, you can add it here */
    this.props.history.push('/redirected');
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          Redirect!!!
        </button>
      </div>
    )
  }
}

export default App;

React-Router 4.x 回答

在我这边,我喜欢有一个历史对象,我什至可以携带外部组件。我喜欢按需导入单个 history.js 文件,然后对其进行操作。

您只需将 BrowserRouter 更改为 Router,并指定 history prop。这不会为您带来任何改变,除了您拥有自己的历史对象,您可以随意操作它。

您需要安装 historyreact-router 使用的库。

用法示例,ES6 表示法:

history.js

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

BasicComponent.js

import React, { Component } from 'react';
import history from './history';

class BasicComponent extends Component {

    goToIndex(e){
        e.preventDefault();
        history.push('/');
    }

    render(){
        return <a href="#" onClick={this.goToIndex}>Previous</a>;
    }
}

如果您必须从实际从 Route 组件呈现的组件导航,您还可以从 props 访问历史记录,例如:

BasicComponent.js

import React, { Component } from 'react';

class BasicComponent extends Component {

    navigate(e){
        e.preventDefault();
        this.props.history.push('/url');
    }

    render(){
        return <a href="#" onClick={this.navigate}>Previous</a>;
    }
}

如果您使用哈希或浏览器历史记录,那么您可以

hashHistory.push('/login');
browserHistory.push('/login');

In React-Router v4 and ES6

您可以使用 withRouterthis.props.history.push

import {withRouter} from 'react-router-dom';

class Home extends Component {

    componentDidMount() {
        this.props.history.push('/redirect-to');
    }
}

export default withRouter(Home);

只需使用this.props.history.push('/where/to/go');

反应路由器 v6

我有一段时间没有接触 React,但要感谢并强调 :

on React-Router 6.0 <Redirect /> changed to <Navigate />

React Router V4

tl:dr;

if (navigate) {
  return <Redirect to="/" push={true} />
}

简单而明确的答案是您需要将 <Redirect to={URL} push={boolean} />setState()

结合使用

push: boolean - when true, redirecting will push a new entry onto the history instead of replacing the current one.


import { Redirect } from 'react-router'

class FooBar extends React.Component {
  state = {
    navigate: false
  }

  render() {
    const { navigate } = this.state

    // Here is the important part
    if (navigate) {
      return <Redirect to="/" push={true} />
    }
   // ^^^^^^^^^^^^^^^^^^^^^^^

    return (
      <div>
        <button onClick={() => this.setState({ navigate: true })}>
          Home
        </button>
      </div>
    )
  }
}

一个完整的例子是here. Read more here

PS。如果您有兴趣,该示例也使用 ES7+ Property Initializers to initialise state. Look here

也许不是最好的解决方案,但它完成了工作:

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

// Create functional component Post
export default Post = () => (
    <div className="component post">

        <button className="button delete-post" onClick={() => {
            // ... delete post
            // then redirect, without page reload, by triggering a hidden Link
            document.querySelector('.trigger.go-home').click();
        }}>Delete Post</button>

        <Link to="/" className="trigger go-home hidden"></Link>

    </div>
);

基本上,与一个操作相关的逻辑(在本例中为 post 删除)最终会调用重定向触发器。这并不理想,因为您将向您的标记添加一个 DOM 节点 'trigger',以便您可以在需要时方便地调用它。此外,您将直接与 DOM 交互,这在 React 组件中可能是不需要的。

不过,这种重定向并不经常需要。因此,组件标记中的一两个额外的隐藏链接不会造成太大伤害,尤其是如果您为它们指定有意义的名称。

在撰写本文时,正确答案对我而言

this.context.router.history.push('/');

但是你需要在你的组件中添加 PropTypes

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

别忘了导入 PropTypes

import PropTypes from 'prop-types';

如果您碰巧通过 react-router-redux 将 RR4 与 redux 配对,也可以选择使用 react-router-redux 中的路由操作创建器。

import { push, replace, ... } from 'react-router-redux'

class WrappedComponent extends React.Component {
  handleRedirect(url, replaceState = true) {
    replaceState
      ? this.props.dispatch(replace(url))
      : this.props.dispatch(push(url))
  }
  render() { ... }
}

export default connect(null)(WrappedComponent)

如果您使用 redux thunk/saga 来管理异步流程,在 redux 操作中导入上述操作创建器并使用 mapDispatchToProps 挂钩到 React 组件可能会更好。

要将 withRouter 与基于 class 的组件一起使用,请尝试以下操作。 不要忘记将导出语句更改为使用 withRouter:

import { withRouter } from 'react-router-dom'

class YourClass extends React.Component {
  yourFunction = () => {
    doSomeAsyncAction(() =>
      this.props.history.push('/other_location')
    )
  }

  render() {
    return (
      <div>
        <Form onSubmit={ this.yourFunction } />
      </div>
    )
  }
}

export default withRouter(YourClass);

在 React Router v4 中,我遵循这两种方式以编程方式进行路由。

  1. this.props.history.push("/something/something")
  2. this.props.history.replace("/something/something")

第二名

Replaces the current entry on the history stack

要在 props 中获取历史记录,您可能需要用

包装您的组件

withRouter

在 React Router v6 中

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

function Invoices() {
  let navigate = useNavigate();
  return (
    <div>
      <NewInvoiceForm
        onSubmit={async event => {
          let newInvoice = await createInvoice(event.target);
          navigate(`/invoices/${newInvoice.id}`);
        }}
      />
    </div>
  );
}

Getting Started with React Router v6

那些在 React Router v4 中实现它时遇到问题的人。

这是一个从 redux 操作中浏览 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>

文件*another_file.jsredux文件

import history from './history'

history.push('/test') // This should change the URL and rerender Test component

感谢 GitHub 上的这条评论: ReactTraining issues comment

React Router v4+

假设您在初始渲染过程中不需要导航(您可以使用 <Redirect> 组件),这就是我们在应用程序中所做的。

定义一个空路由,returns null。这将允许您访问历史对象。您需要在定义 Router 的顶层执行此操作。

现在您可以做在 history 上可以做的所有事情,例如 history.push()history.replace()history.go(-1) 等!

import React from 'react';
import { HashRouter, Route } from 'react-router-dom';

let routeHistory = null;

export function navigateTo(path) {
  if(routeHistory !== null) {
    routeHistory.push(path);
  }
}

export default function App(props) {
  return (
    <HashRouter hashType="noslash">
      <Route
        render={({ history }) => {
          routeHistory = history;
          return null;
        }}
      />
      {/* Rest of the App */}
    </HashRouter>
  );
}

这对我有用,不需要特殊导入:

<input 
  type="button" 
  name="back" 
  id="back" 
  class="btn btn-primary" 
  value="Back" 
  onClick={() => { this.props.history.goBack() }} 
/>

您还可以在无状态组件中使用 useHistory 挂钩。文档中的示例:

import { useHistory } from "react-router"

function HomeButton() {
  const history = useHistory()

  return (
    <button type="button" onClick={() => history.push("/home")}>
      Go home
    </button>
  )
}

Note: Hooks were added in react-router@5.1.0 and require react@>=16.8

在我的回答中,有三种不同的方法可以以编程方式重定向到路由。已经介绍了一些解决方案,但以下解决方案仅针对 功能组件 以及附加的演示应用程序。

使用以下版本:

react: 16.13.1

react-dom: 16.13.1

react-router: 5.2.0

react-router-dom: 5.2.0

typescript: 3.7.2

配置:

所以首先解决方案是使用HashRouter,配置如下:

<HashRouter>
    // ... buttons for redirect

    <Switch>
      <Route exact path="/(|home)" children={Home} />
      <Route exact path="/usehistory" children={UseHistoryResult} />
      <Route exact path="/withrouter" children={WithRouterResult} />
      <Route exact path="/redirectpush" children={RedirectPushResult} />
      <Route children={Home} />
    </Switch>
</HashRouter>

来自 the documentation 关于 <HashRouter>:

A <Router> that uses the hash portion of the URL (i.e. window.location.hash) to keep your UI in sync with the URL.

解法:

  1. 使用<Redirect>推送使用useState:

在功能组件中使用(RedirectPushAction component from my repository) we can use useState 来处理重定向。棘手的部分是一旦重定向发生,我们需要将 redirect 状态设置回 false。通过使用setTimeOut 延迟 0 我们正在等待 React 将 Redirect 提交给 DOM 然后取回按钮以便下次使用它。

请在下面找到我的示例:

const [redirect, setRedirect] = useState(false);
const handleRedirect = useCallback(() => {
    let render = null;
    if (redirect) {
        render = <Redirect to="/redirectpush" push={true} />

        // In order wait until committing to the DOM
        // and get back the button for clicking next time
        setTimeout(() => setRedirect(false), 0);
    }
    return render;
}, [redirect]);

return <>
    {handleRedirect()}
    <button onClick={() => setRedirect(true)}>
        Redirect push
    </button>
</>

来自 <Redirect> 文档:

Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do.

  1. 使用 useHistory 钩子:

在我的解决方案中有一个名为 UseHistoryAction 的组件,它表示以下内容:

let history = useHistory();

return <button onClick={() => { history.push('/usehistory') }}>
    useHistory redirect
</button>

The useHistory hook gives us access to the history object which helps us programmatically navigate or change routes.

  1. 使用withRouter,从props得到history

创建了一个名为WithRouterAction的组件,显示如下:

const WithRouterAction = (props:any) => {
    const { history } = props;

    return <button onClick={() => { history.push('/withrouter') }}>
        withRouter redirect
    </button>
}

export default withRouter(WithRouterAction);

正在阅读 withRouter 文档:

You can get access to the history object's properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.

演示:

为了更好地展示我已经用这些示例构建了一个 GitHub 存储库,请在下面找到它:

React Router Programmatically Redirect Examples

react-router-dom: 5.1.2

  • 本网站有 3 个页面,所有页面均在浏览器中动态呈现。

  • 虽然页面永远不会刷新,但请注意 React Router 如何 在您浏览网站时使 URL 保持最新。这个 保留浏览器历史记录,确保像后面这样的事情 按钮和书签正常工作

  • A Switch 遍历所有子节点 元素并呈现第一个其路径 匹配当前 URL。随时使用 你有多条路线,但你只想要一条 其中一次渲染

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";



export default function BasicExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>

        <hr />

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

// You can think of these components as "pages"
// in your app.

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function About() {
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}

function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}```

试试 React Hook Router,“react-router 的现代替代品”:

import { useRoutes, usePath, A} from "hookrouter";

要回答 OP 关于通过 select 框链接的问题,您可以这样做:

navigate('/about');

更新答案

我认为 React Hook Router 是一个很好的入门工具包,帮助我学习了路由,但我已经更新到 React Router 以了解其历史记录和查询参数处理。

import { useLocation, useHistory } from 'react-router-dom';


const Component = (props) => {
    const history = useHistory();

    // Programmatically navigate
    history.push(newUrlString);
}

您将要导航的位置推入 location.history。

带有钩子的 React Router v6

import {useNavigate} from 'react-router-dom';
let navigate = useNavigate();
navigate('home');

并浏览浏览器历史记录,

navigate(-1); ---> Go back
navigate(1);  ---> Go forward
navigate(-2); ---> Move two steps backward.

以编程方式在 class-based 个组件中导航。

import { Redirect } from "react-router-dom";

class MyComponent extends React.Component{
    state = {rpath: null}

    const goTo = (path) => this.setState({rpath: path});

    render(){
        if(this.state.rpath){
            return <Redirect to={this.state.rpath}/>
        }
        .....
        .....
    }
}

如果您使用的是较新版本的 React,使用“useHistory”挂钩是最佳选择。

这已经提到我们可以使用 useNavigate()

在上次更新的 React Router V6(不包括 useHistory)中导航
import { useNavigate } from 'react-router-dom';

const myComponent = () => {
  const navigate = useNavigate();
  navigate('my_url');
  ...
}

但我在这里找不到的是调用导航出 React 组件,例如在 redux-saga 函数内导航页面到另一个页面。以防万一,如果你有同样的问题,这是我找到的。

在你的根组件中(我称之为 <App />

import { useNavigate } from 'react-router-dom';
import useBus from 'use-bus';

const App = () => {
  const navigate = useNavigate();
  useBus('@@ui/navigate', (action) => navigate(action.payload.url), []);
  ...
}

没有 React 组件(在我的例子中是 redux-saga 函数)

import { dispatch } from 'use-bus';

dispatch({ type: '@@ui/navigate', payload: { url: '/404' } });

希望对您有所帮助!

对于已经在使用 React Router v6 的用户,可以使用 react-router 提供的 useNavigate 钩子来完成。

使用这个钩子导航非常简单:

import { generatePath, useNavigate } from 'react-router';

navigate(-1); // navigates back
navigate('/my/path'); // navigates to a specific path
navigate(generatePath('my/path/:id', { id: 1 })); // navigates to a dynamic path, generatePath is very useful for url replacements

最新 react-router-dom v6

useHistory() 替换为 useNavigate()

您需要使用:

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

为了简单起见,只需使用 useNavigate 最新版本的 react

Newfile.js

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

const Newfile = () => {
    const navigate = useNavigate();
   
    ....

    navigate("yourdesiredlocation");

    ....
}

export default Newfile;

在您的代码中使用上述 useNavigate 功能。