How to push to History in React Router v4?

2018-12-31 17:44发布

In the current version of React Router (v3) I can accept a server response and use browserHistory.push to go to the appropriate response page. However, this isn't available in v4, and I'm not sure what the appropriate way to handle this is.

In this example, using Redux, components/app-product-form.js calls this.props.addProduct(props) when a user submits the form. When the server returns a success, the user is taken to the Cart page.

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

How can I make a redirect to the Cart page from function for React Router v4?

17条回答
流年柔荑漫光年
2楼-- · 2018-12-31 18:04

Here's my hack (this is my root-level file, with a little redux mixed in there - though I'm not using 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')
)

I can then use window.appHistory.push() anywhere I want (for example, in my redux store functions/thunks/sagas, etc) I had hoped I could just use window.customHistory.push() but for some reason react-router never seemed to update even though the url changed. But this way I have the EXACT instance react-router uses. I don't love putting stuff in the global scope, and this is one of the few things I'd do that with. But it's better than any other alternative I've seen IMO.

查看更多
与风俱净
3楼-- · 2018-12-31 18:06

you can use it like this as i do it for login and manny different things

class Login extends Component {
  constructor(props){
    super(props);
    this.login=this.login.bind(this)
  }


  login(){
this.props.history.push('/dashboard');
  }


render() {

    return (

   <div>
    <button onClick={this.login}>login</login>
    </div>

)
查看更多
残风、尘缘若梦
4楼-- · 2018-12-31 18:11

this.context.history.push will not work.

I managed to get push working like this:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

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

}
查看更多
墨雨无痕
5楼-- · 2018-12-31 18:11

Simplest way in React Router 4 is to use

this.props.history.push('/new/url');

But to use this method, your existing component should have access to history object. We can get access by

  1. If your component is linked to Route directly, then your component already has access to history object.

    eg:

    <Route path="/profile" component={ViewProfile}/>
    

    Here ViewProfile has access to history.

  2. If not connected to Route directly.

    eg:

    <Route path="/users" render={() => <ViewUsers/>}
    

    Then we have to use withRouter, a heigher order fuction to warp the existing component.

    Inside ViewUsers component

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

    • export default withRouter(ViewUsers);

    That's it now, your ViewUsers component has access to history object.

查看更多
素衣白纱
6楼-- · 2018-12-31 18:15

React Router v4 is fundamentally different from v3 (and earlier) and you cannot do browserHistory.push() like you used to.

This discussion seems related if you want more info:

  • Creating a new browserHistory won't work because <BrowserRouter> creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>.
  • browserHistory is not exposed by react-router in v4, only in v2.

Instead you have a few options to do this:

  • Use the withRouter high-order component

    Instead you should use the withRouter high order component, and wrap that to the component that will push to history. For example:

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

    Check out the official documentation for more info:

    You can get access to the history object’s properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will re-render its component every time the route changes with the same props as <Route> render props: { match, location, history }.


  • Use the context API

    Using the context might be one of the easiest solutions, but being an experimental API it is unstable and unsupported. Use it only when everything else fails. Here's an example:

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

    Have a look at the official documentation on context:

    If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.

    If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.

查看更多
登录 后发表回答