如何在React Router v4中推送到历史记录?

Chr*_*ris 315 reactjs react-router react-router-v4

在当前版本的React Router(v3)中,我可以接受服务器响应并使用它browserHistory.push来转到相应的响应页面.但是,这在v4中不可用,我不确定处理它的适当方法是什么.

在此示例中,使用Redux,在用户提交表单时调用components/app-product-form.jsthis.props.addProduct(props).当服务器返回成功时,用户将进入购物车页面.

// actions/index.js
export function addProduct(props) {
  return dispatch =>
    axios.post(`${ROOT_URL}/cart`, props, config)
      .then(response => {
        dispatch({ type: types.AUTH_USER });
        localStorage.setItem('token', response.data.token);
        browserHistory.push('/cart'); // no longer in React Router V4
      });
}
Run Code Online (Sandbox Code Playgroud)

如何从React Router v4的功能重定向到Cart页面?

Chr*_*ris 319

React Router v4与v3(及更早版本)根本不同,你不能browserHistory.push()像以前那样做.

如果您想了解更多信息,此讨论似乎相关:

  • 创建新的browserHistory将无法工作,因为<BrowserRouter>创建自己的历史记录实例,并侦听其中的更改.因此,不同的实例将更改URL但不更新<BrowserRouter>.
  • browserHistory 在v4中,反应路由器不会公开,仅在v2中.

相反,您有几个选项可以做到这一点:

  • 使用withRouter高阶组件

    相反,您应该使用withRouter高阶组件,并将其包装到将推送到历史记录的组件.例如:

    import React from "react";
    import { withRouter } from "react-router-dom";
    
    class MyComponent extends React.Component {
      ...
      myFunction() {
        this.props.history.push("/some/Path");
      }
      ...
    }
    export default withRouter(MyComponent);
    
    Run Code Online (Sandbox Code Playgroud)

    查看官方文档了解更多信息:

    你可以访问history对象的属性和最近<Route>match经withRouter高阶组件.withRouter将在每次路径更改时重新渲染其组件,并使用与<Route>渲染道具相同的道具:{ match, location, history }.


  • 使用contextAPI

    使用上下文可能是最简单的解决方案之一,但作为实验性API,它不稳定且不受支持.仅在其他一切都失败时使用它.这是一个例子:

    import React from "react";
    import PropTypes from "prop-types";
    
    class MyComponent extends React.Component {
      static contextTypes = {
        router: PropTypes.object
      }
      constructor(props, context) {
         super(props, context);
      }
      ...
      myFunction() {
        this.context.router.history.push("/some/Path");
      }
      ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

    查看有关上下文的官方文档:

    如果您希望应用程序稳定,请不要使用上下文.它是一个实验性的API,很可能会在未来的React版本中破解.

    如果您坚持使用上下文尽管存在这些警告,请尝试将上下文的使用隔离到一个小区域,并尽可能避免在可能的情况下直接使用上下文API,以便在API更改时更容易升级.

  • 这并没有回答被问到的问题,即如何访问history.push组件的OUTSIDE.使用withRouter或上下文不是组件外部的选项. (44认同)
  • 是的,我确实尝试过.谢谢你的询问.:-)那么如何将上下文纳入此动作函数?到目前为止,它还未定义. (9认同)
  • @Stu这应该工作`this.context.router.history.push('/ path');` (5认同)
  • 在我的情况下使用withRouter帮助的解决方案,谢谢. (4认同)
  • 从 React 16.3 开始,上下文 API 不再是实验性的。React 的博客文章 [React v16.3.0:新的生命周期和上下文 API](https://reactjs.org/blog/2018/03/29/react-v-16-3.html) 了解有关该版本的更多信息。 (2认同)

Ole*_*sky 260

您可以使用history组件外部的方法.请尝试以下方式.

首先,创建一个history使用历史包的对象:

// src/history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory();
Run Code Online (Sandbox Code Playgroud)

然后把它包起来<Router>(请注意,你应该使用import { Router }而不是import { BrowserRouter as Router }):

// src/index.jsx

// ...
import { Router, Route, Link } from 'react-router-dom';
import history from './history';

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <div>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/login">Login</Link></li>
        </ul>
        <Route exact path="/" component={HomePage} />
        <Route path="/login" component={LoginPage} />
      </div>
    </Router>
  </Provider>,
  document.getElementById('root'),
);
Run Code Online (Sandbox Code Playgroud)

从任何地方更改您当前的位置,例如:

// src/actions/userActionCreators.js

// ...
import history from '../history';

export function login(credentials) {
  return function (dispatch) {
    return loginRemotely(credentials)
      .then((response) => {
        // ...
        history.push('/');
      });
  };
}
Run Code Online (Sandbox Code Playgroud)

UPD:您还可以在React Router FAQ中看到略有不同的示例.

  • 我试图像@OlegBelostotsky所说的那样做,但是在`history.push('some path')`之后,URL会改变,但页面不会改变.我必须在我的代码的某些部分之后放置`window.location.reload()`才能使它工作.但是,在一个实例中,我必须保留redux状态树,然后重新加载会破坏它.还有其他方法吗? (20认同)
  • @idunno 尝试使用 `withRouter` 高阶组件。 (3认同)
  • 对不起,我的反对票:)。虽然这也应该有效,但正确的处理方法是 Chris 的回答:/sf/answers/2990123881/。 (2认同)
  • 确保您使用[文档](https://github.com/ReactTraining/history)中所示的正确版本的历史记录。在将历史记录 v5 与 React Router v5 一起使用时,我遇到了与 @sdabrutas 类似的问题(URL 推送到历史记录,但页面没有更改)。将历史记录降级到 v4 完全解决了这个问题。 (2认同)

Mic*_*ski 23

我就这样做了:

import React, {Component} from 'react';

export default class Link extends Component {
    constructor(props) {
        super(props);
        this.onLogout = this.onLogout.bind(this);
    }
    onLogout() {
        this.props.history.push('/');
    }
    render() {
        return (
            <div>
                <h1>Your Links</h1>
                <button onClick={this.onLogout}>Logout</button>
            </div>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

使用this.props.history.push('/cart');重定向到购物车页面将被保存在历史记录对象.

享受,迈克尔.

  • 这并没有回答被问到的问题,即如何访问history.push组件的OUTSIDE.在组件外部使用this.props.history不是一个选项. (12认同)
  • 是的,看起来在组件内您可以很好地进行推送。影响组件外部导航的唯一方法是重定向。 (2认同)

小智 21

根据React Router v4文档 - Redux Deep Integration会话

需要深度整合:

"能够通过调度行动导航"

但是,他们建议这种方法作为"深度整合"的替代方案:

"而不是分派行动进行导航,您可以将提供的历史记录对象传递给您的行为并在那里进行导航."

因此,您可以使用withRouter高阶组件包装组件:

export default withRouter(connect(null, { actionCreatorName })(ReactComponent));

这会将历史API传递给道具.所以你可以调用动作创建者将历史作为参数传递.例如,在ReactComponent中:

onClick={() => {
  this.props.actionCreatorName(
    this.props.history,
    otherParams
  );
}}
Run Code Online (Sandbox Code Playgroud)

然后,在你的actions/index.js里面:

export function actionCreatorName(history, param) {
  return dispatch => {
    dispatch({
      type: SOME_ACTION,
      payload: param.data
    });
    history.push("/path");
  };
}
Run Code Online (Sandbox Code Playgroud)


Saa*_*ran 19

在React Router 4中最简单的方法就是使用

this.props.history.push('/new/url');
Run Code Online (Sandbox Code Playgroud)

但是要使用此方法,现有组件应该可以访问history对象.我们可以通过访问

  1. 如果您的组件Route直接链接,那么您的组件已经可以访问history对象.

    例如:

    <Route path="/profile" component={ViewProfile}/>
    
    Run Code Online (Sandbox Code Playgroud)

    这里ViewProfile有访问权限history.

  2. 如果没有Route直接连接.

    例如:

    <Route path="/users" render={() => <ViewUsers/>}
    
    Run Code Online (Sandbox Code Playgroud)

    然后我们必须使用withRouter更高级的功能来扭曲现有组件.

    内部 ViewUsers组件

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

    • export default withRouter(ViewUsers);

    就是这样,你的ViewUsers组件可以访问history对象.

UPDATE

2- 在这种情况下,将所有路径传递props给您的组件,然后this.props.history即使没有a ,我们也可以从组件访问HOC

例如:

<Route path="/users" render={props => <ViewUsers {...props} />}
Run Code Online (Sandbox Code Playgroud)

  • 第二种方法对我有用. (3认同)

Khr*_*rok 18

讨厌的问题,花了我很多时间,但最终,我这样解决了:

包裹您的容器withRouter并将历史记录传递给您的 mapDispatchToProps功能.在操作中使用history.push('/ url')进行导航.

行动:

export function saveData(history, data) {
  fetch.post('/save', data)
     .then((response) => {
       ...
       history.push('/url');
     })
};
Run Code Online (Sandbox Code Playgroud)

容器:

import { withRouter } from 'react-router-dom';
...
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    save: (data) => dispatch(saveData(ownProps.history, data))}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Container));
Run Code Online (Sandbox Code Playgroud)

这对React Router v4.x有效.


小智 7

this.context.history.push 不管用.

我成功地推动了这样的工作:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

    if (this.props.auth.success) {
        this.context.router.history.push("/some/Path")
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 这并没有回答被问到的问题,即如何访问history.push组件的OUTSIDE.在组件外部使用this.context不是一个选项. (7认同)

use*_*377 6

在这种情况下,你将道具传递给你的thunk.所以你可以简单地打电话

props.history.push('/cart')
Run Code Online (Sandbox Code Playgroud)

如果不是这种情况,您仍然可以从组件中传递历史记录

export function addProduct(data, history) {
  return dispatch => {
    axios.post('/url', data).then((response) => {
      dispatch({ type: types.AUTH_USER })
      history.push('/cart')
    })
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

I offer one more solution in case it is worthful for someone else.

I have a history.js file where I have the following:

import createHistory from 'history/createBrowserHistory'
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.push(...args))
export default history
Run Code Online (Sandbox Code Playgroud)

Next, on my Root where I define my router I use the following:

import history from '../history'
import { Provider } from 'react-redux'
import { Router, Route, Switch } from 'react-router-dom'

export default class Root extends React.Component {
  render() {
    return (
     <Provider store={store}>
      <Router history={history}>
       <Switch>
        ...
       </Switch>
      </Router>
     </Provider>
    )
   }
  }
Run Code Online (Sandbox Code Playgroud)

Finally, on my actions.js I import History and make use of pushLater

import history from './history'
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)
Run Code Online (Sandbox Code Playgroud)

This way, I can push to new actions after API calls.

Hope it helps!


Moh*_*mad 6

注意不要使用react-router@5.2.0react-router-dom@5.2.0with history@5.0.0。URL 将在history.push或任何其他推送到历史记录说明之后更新,但导航不适用于react-router. 用于npm install history@4.10.1更改历史版本。请参阅升级到 v 5 后反应路由器不工作

我认为这个问题是在推动历史发生时发生的。例如,<NavLink to="/apps">在 NavLink.js 中遇到一个消耗<RouterContext.Consumer>. context.location当推送到历史发生时,正在更改为具有操作和位置属性的对象。因此currentLocation.pathname为 null 以匹配路径。


Had*_*Abu 5

现在,通过react-router v5,您可以使用useHistory生命周期挂钩,如下所示:

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

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

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

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,访问:https : //reacttraining.com/react-router/web/api/Hooks/usehistory

  • 很好,但不能在 redux 操作类中使用钩子,因为它们不是 React 组件/函数 (9认同)
  • 我需要有什么具体的方法来设置它吗?我正在调用以下“let History = useHistory();”,但是当我尝试查看 useHistory 是什么“console.log(”时,出现“对象不可调用”错误useHistory)` 它显示为未定义。使用 `"react-router-dom": "^5.0.1"` (2认同)

小智 5

我在同一主题上遇到了困难。我正在使用react-router-dom 5、Redux 4 和BrowserRouter。我更喜欢基于函数的组件和钩子。

你像这样定义你的组件

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

const Component = () => {
  ...
  const history = useHistory();
  dispatch(myActionCreator(otherValues, history));
};

Run Code Online (Sandbox Code Playgroud)

你的动作创建者正在关注

const myActionCreator = (otherValues, history) => async (dispatch) => {
  ...
  history.push("/path");
}
Run Code Online (Sandbox Code Playgroud)

如果不需要异步,您当然可以拥有更简单的动作创建器