Comment pousser à L'histoire dans React Router V4?

Dans la version actuelle de React Router (v3), je peux accepter une réponse du serveur et utiliser browserHistory.push pour accéder à la page de réponse appropriée. Cependant, ce n'est pas disponible dans v4, et je ne suis pas sûr de la façon appropriée de gérer cela.

Dans cet exemple, à l'aide de Redux, composants/app-produit-forme.js appels this.props.addProduct(props) lorsqu'un utilisateur soumet le formulaire. Lorsque le serveur renvoie un succès, l'utilisateur est redirigé vers la page Panier.

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

Comment puis-je faire une redirection vers la page du Panier à partir de fonction pour réagir Routeur v4?

180
demandé sur Chris 2017-03-09 19:56:11

15 réponses

Vous pouvez utiliser les méthodes history en dehors de vos composants. Essayez de la manière suivante.

Tout d'abord, créez un objet history Utilisé le paquet historique :

// src/history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory();

, Puis l'envelopper dans <Router> (veuillez noter, vous devez utiliser import { Router } au lieu de 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'),
);

Changez votre emplacement actuel de n'importe quel endroit, par exemple:

// src/actions/userActionCreators.js

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

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

UPD: Vous pouvez également voir un exemple légèrement différent de Réagir Routeur FAQ.

95
répondu Oleg Belostotsky 2018-01-01 09:11:49

React Router V4 est fondamentalement différent de v3 (et plus tôt) et vous ne pouvez pas faire browserHistory.push() comme vous le faisiez auparavant.

Cette discussion semble liée, si vous voulez plus d'info:

  • la création d'un nouveau browserHistory ne fonctionnera pas car <BrowserRouter> crée sa propre instance d'historique et écoute les modifications à ce sujet. Ainsi, une instance différente changera l'url mais ne mettra pas à jour le <BrowserRouter>.
  • browserHistory n'est pas exposé par react-router dans v4, seulement dans v2.

Au lieu de cela, vous avez quelques options pour faire ceci:

  • Utilisez le composant withRouter d'ordre Élevé

    À la place, vous devez utiliser le composant withRouter d'ordre élevé, et l'envelopper dans le composant qui va pousser à l'historique. Par exemple:

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

    Découvrez la la documentation officielle pour plus d'infos:

    , Vous pouvez obtenir l'accès à la history propriétés de l'objet et les plus proches <Route>'s match via le composant d'ordre supérieur withRouter. withRouter restituera son composant chaque fois que la route change avec les mêmes accessoires que <Route> rendre les accessoires: { match, location, history }.


  • Utilisez L'API context

    L'Utilisation du contexte peut être l'une des solutions les plus faciles, mais étant une API expérimentale, elle est instable et non prise en charge. Utiliser seulement quand tout le reste échoue. Voici un exemple:

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

    Jetez un oeil à l'la documentation officielle le contexte:

    Si vous voulez que votre application soit stable, n'utilisez pas context. C'est une API expérimentale et il est susceptible de casser dans les futures versions de React.

    Si vous insistez pour utiliser context malgré ces avertissements, essayez d'isoler votre utilisation de context dans une petite zone et évitez d'utiliser l'API context directement si possible afin qu'il soit plus facile de mettre à niveau lorsque l'API change.

220
répondu Chris 2018-04-26 10:41:29

Voici comment je l'ai fait:

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

Utilisez this.props.history.push('/cart'); pour rediriger vers la page panier, il sera enregistré dans l'objet historique.

Profiter, Michael.

17
répondu Michael Horojanski 2018-07-22 14:53:19

Selon Réagir Routeur v4 documentation - Redux l'Intégration en Profondeur de la session

Une intégration profonde est nécessaire pour:

"Être capable de naviguer en répartissant des actions"

Cependant, ils recommandent cette approche comme alternative à l '"intégration profonde":

" plutôt que de répartir les actions pour naviguer, vous pouvez passer l'objet historique fourni pour acheminer les composants vers vos actions et naviguer avec lui."

Donc vous peut envelopper votre composant avec le composant withRouter high order:

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

Qui passera L'API d'historique aux accessoires. Ainsi, vous pouvez appeler le créateur d'action en passant l'historique en tant que paramètre. Par exemple, dans votre ReactComponent:

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

Ensuite, à l'intérieur de vos actions / index.js:

export function actionCreatorName(history, param) {
  return dispatch => {
    dispatch({
      type: SOME_ACTION,
      payload: param.data
    });
    history.push("/path");
  };
}
13
répondu alainbex 2017-08-13 23:51:43

Question méchante, m'a pris beaucoup de temps, mais finalement, je l'ai résolu de cette façon:

Enveloppez votre conteneur avec withRouter et passez l'historique à votre action dans la fonction mapDispatchToProps. En action utiliser l'historique.appuyez sur ('/url') pour naviguer.

Action:

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

Conteneur:

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

Ceci est valable pour les Réagir Routeur v4.x .

12
répondu Khrystyna Skvarok 2018-03-12 07:26:36

this.context.history.push ne fonctionnera pas.

J'ai réussi à faire fonctionner push comme ceci:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

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

}
6
répondu Aimar 2017-05-18 11:14:59

Dans ce cas, vous passez des accessoires à votre thunk. Donc, vous pouvez simplement appeler

props.history.push('/cart')

Si ce n'est pas le cas, vous pouvez toujours passer l'historique de votre composant

export function addProduct(data, history) {
  return dispatch => {
    axios.post('/url', data).then((response) => {
      dispatch({ type: types.AUTH_USER })
      history.push('/cart')
    })
  }
}
4
répondu user3812377 2017-08-09 09:04:59

J'offre une solution de plus au cas où cela vaudrait la peine pour quelqu'un d'autre.

J'ai un fichier history.js où j'ai ce qui suit:

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

Ensuite, sur ma racine où je définis mon routeur, j'utilise ce qui suit:

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

Enfin, sur mon actions.js j'importe L'historique et j'utilise pushLater

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

De cette façon, je peux pousser vers de nouvelles actions après les appels D'API.

J'espère que ça aide!

3
répondu Diego 2017-10-30 12:15:55

Si vous utilisez Redux, je vous recommande d'utiliser le paquet npm react-router-redux . Il vous permet d'envoyer des actions de navigation de magasin Redux.

Vous devez créer un magasin comme décrit dans leur fichier Readme .

Le cas d'utilisation le plus simple:

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

this.props.dispatch(push('/second page'));

Deuxième cas d'utilisation avec Conteneur/Composant:

Conteneur:

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

Composant:

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
    );
  }
}
2
répondu Black 2017-09-11 13:04:09

J'ai pu accomplir ceci en utilisant bind(). Je voulais cliquer sur un bouton dans index.jsx, poster des données sur le serveur, évaluer la réponse et rediriger vers success.jsx. Voici comment j'ai travaillé que sur...

index.jsx:

import React, { Component } from "react"
import { postData } from "../../scripts/request"

class Main extends Component {
    constructor(props) {
        super(props)
        this.handleClick = this.handleClick.bind(this)
        this.postData = postData.bind(this)
    }

    handleClick() {
        const data = {
            "first_name": "Test",
            "last_name": "Guy",
            "email": "test@test.com"
        }

        this.postData("person", data)
    }

    render() {
        return (
            <div className="Main">
                <button onClick={this.handleClick}>Test Post</button>
            </div>
        )
    }
}

export default Main

request.js:

import { post } from "./fetch"

export const postData = function(url, data) {
    // post is a fetch() in another script...
    post(url, data)
        .then((result) => {
            if (result.status === "ok") {
                this.props.history.push("/success")
            }
        })
}

success.jsx:

import React from "react"

const Success = () => {
    return (
        <div className="Success">
            Hey cool, got it.
        </div>
    )
}

export default Success

, Donc par la liaison this à postData dans index.jsx, j'ai été en mesure d'accéder this.props.history dans request.js... ensuite, je peux réutiliser cette fonction dans différents composants, juste pour m'assurer que je me souviens d'inclure this.postData = postData.bind(this) dans le constructor().

2
répondu skwidbreth 2017-11-21 16:40:57

Utiliser Le Rappel. Il a travaillé pour moi!

export function addProduct(props, callback) {
  return dispatch =>
    axios.post(`${ROOT_URL}/cart`, props, config)
    .then(response => {
    dispatch({ type: types.AUTH_USER });
    localStorage.setItem('token', response.data.token);
    callback();
  });
}

Dans le composant, il vous suffit d'ajouter le rappel

this.props.addProduct(props, () => this.props.history.push('/cart'))
2
répondu Priyansh Rastogi 2018-03-11 19:53:16

Voici mon hack (c'est mon fichier au niveau racine, avec un peu de redux mélangé là-bas-bien que je n'utilise pas 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')
)

Je peux ensuite utiliser window.appHistory.push() où je veux (par exemple, dans mes fonctions de magasin redux/thunks/sagas, etc) j'avais espéré pouvoir utiliser window.customHistory.push() mais pour une raison quelconque react-router n'a jamais semblé mettre à jour même si l'url a changé. Mais de cette façon, j'ai L'instance exacte react-router utilise. Je n'aime pas mettre des choses dans la portée mondiale, et c'est l'une des rares choses avec lesquelles je ferais ça. Mais c'est mieux que toute autre alternative que J'ai vu IMO.

1
répondu Joao 2017-08-20 17:32:15

Vous pouvez l'utiliser comme ceci comme je le fais pour login et manny différentes choses

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>

)
0
répondu jadlmir 2018-03-01 09:33:28
/*Step 1*/
myFunction(){  this.props.history.push("/home"); }
/**/
 <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
 Home</button>
0
répondu David Bagdasaryan 2018-03-10 08:11:07

Première étape enveloppez votre application dans le routeur

import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById('root'));

Maintenant, toute mon application aura accès à BrowserRouter. Deuxième étape j'importe la Route et puis passe ces accessoires. Probablement dans un de vos fichiers.

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

//lots of code here

//somewhere in my render function

    <Route
      exact
      path="/" //put what your file path is here
      render={props => (
      <div>
        <NameOfComponent
          {...props} //this will pass down your match, history, location objects
        />
      </div>
      )}
    />

Maintenant, si je lance la console.journal(ce.props) dans mon fichier JS composant que je devrais obtenir quelque chose qui ressemble à ceci

{match: {…}, location: {…}, history: {…}, //other stuff }

Étape 2 je peux accéder à l'objet historique pour changer mon emplacement

//lots of code here relating to my whatever request I just ran delete, put so on

this.props.history.push("/") // then put in whatever url you want to go to

Aussi, je suis juste un étudiant en codage bootcamp, donc je ne suis pas un expert, mais je sais vous pouvez également utiliser

window.location = "/" //wherever you want to go

Corrigez-moi si je me trompe, mais quand j'ai testé cela, il a rechargé toute la page que je pensais avoir vaincue tout le point d'utiliser React.

0
répondu Marshall Lanners 2018-09-21 05:49:08