如何在React Router v4中推送历史logging?

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

在这个例子中,使用Redux时, 组件/ app-product-form.js在用户提交表单时调用this.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 }); } 

如何从React Router v4的函数redirect到购物车页面?

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

这个讨论似乎相关,如果你想要更多的信息:

  • 创build一个新的browserHistory将不起作用,因为<BrowserRouter>创build自己的历史实例,并监听更改。 所以不同的实例会改变url,但不会更新<BrowserRouter>
  • browserHistory不会被v4中的react-router暴露,只能在v2中显示。

相反,你有几个select来做到这一点:

  • 使用withRouter高阶组件

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

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

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

    您可以通过withRouter高阶组件访问history对象的属性和最近的<Route> match 。 当路由改变时,路由器会重新渲染它的组件,path与<Route>渲染道具{ match, location, history }


  • 使用context API

    使用上下文可能是最简单的解决scheme之一,但作为一个实验性的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"); } ... } 

    看看上下文的官方文档

    如果你想要你的应用程序是稳定的,不要使用上下文。 这是一个实验性的API,它可能在未来的React版本中被打破。

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

我是这么做的:

 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> ); } } 

使用this.props.history.push('/cart'); redirect到购物车页面,它将被保存在历史对象中。

享受,迈克尔。

您可以在组件外使用history方法。 试试以下方法。

首先,创build一个使用HTML5历史API的历史对象:

 // src/history.js import createHistory from 'history/createBrowserHistory'; export default createHistory(); 

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

 // src/index.jsx // ... import { Router, Link, Route } 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={Home} /> <Route path="/login" component={Login} /> </div> </Router> </Provider>, document.getElementById('root'), ); 

在任何操作中更改当前位置,例如:

 // src/actions/sessionActions.js // ... import history from '../history'; export function login(credentials) { return function (dispatch) { return SessionApi.login(credentials) .then((response) => { // ... history.push('/'); // ... }).catch((error) => { throw error; }); }; } 

UPD :你也可以在React Router FAQ中看到一个稍微不同的例子。

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") } } 

在这种情况下,你正在将道具传递给你。 所以你可以直接打电话

 props.history.push('/cart') 

如果不是这种情况,您仍然可以传递组件的历史logging

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

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

深度整合需要:

“能够通过调度操作进行导航”

但是,他们推荐这种方法作为“深度融合”的替代方法:

“而不是调度行动来导航你可以传递提供的历史对象路由组件到您的行动,并在那里导航。

所以你可以使用withRouter高阶组件包装你的组件:

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

将历史API传递给道具。 所以你可以把行为创build者作为一个parameter passing给历史。 例如,在你的ReactComponent里面:

 onClick={() => { this.props.actionCreatorName( this.props.history, otherParams ); }} 

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

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

如果你使用Redux,那么我会推荐使用npm package react-router-redux 。 它允许您分派Redux商店导航操作。

您必须按照自述文件中所述创build商店。

最简单的用例:

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

容器/组件的第二个用例:

容器:

 import { connect } from 'react-redux'; import { push } from 'react-router-redux'; import Form from '../components/Form'; const mapDispatchToProps = dispatch => ({ changeUrl: url => dispatch(push(url)), }); export default connect(null, mapDispatchToProps)(Form); 

零件:

 import React, { Component } from 'react'; import PropTypes from 'prop-types'; export default class Form extends Component { handleClick = () => { this.props.changeUrl('/secondPage'); }; render() { return ( <div> <button onClick={this.handleClick}/> </div>Readme file ); } } 

讨厌的问题,花了我很多时间,但最终,我解决了下一个方法:

使用withRouter包装你的容器,并在mapDispatchToProps函数mapDispatchToProps历史传递给你的动作。 在操作中使用history.push('/ url')来导航。

行动:

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

容器:

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

这对React Router v4.x有效。

这里是我的黑客(这是我的根级文件,有一点点混合在那里 – 虽然我没有使用react-router-redux ):

 const store = configureStore() const customHistory = createBrowserHistory({ basename: config.urlBasename || '' }) ReactDOM.render( <Provider store={store}> <Router history={customHistory}> <Route component={({history}) => { window.appHistory = history return ( <App /> ) }}/> </Router> </Provider>, document.getElementById('root') ) 

然后我可以使用window.appHistory.push()我想要的任何地方(例如,在我的redux商店function/ thunks / sagas等)我希望我可以只使用window.customHistory.push()但出于某种原因react-router似乎从来没有更新,即使URL更改。 但是这样我有EXACT实例react-router使用。 我不喜欢把东西放在全球范围内,这是我所要做的less数事情之一。 但是比其他任何我见过的IMO都好。

我提供了一个更好的解决scheme,以防别人有价值。

我有一个history.js文件,我有以下几点:

 import createHistory from 'history/createBrowserHistory' const history = createHistory() history.pushLater = (...args) => setImmediate(() => history.push(...args)) export default history 

接下来,在我的根我定义我的路由器我使用以下内容:

 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> ) } } 

最后,在我的actions.js导入History,并使用pushLater

 import history from './history' export const login = createAction( ... history.pushLater({ pathname: PATH_REDIRECT_LOGIN }) ...) 

这样,我可以在API调用之后推动新的操作。

希望它有帮助!