web-dev-qa-db-fra.com

Comment passer à l'historique 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 la v4, et je ne suis pas sûr de la manière appropriée de le gérer.

Dans cet exemple, en utilisant Redux, components/app-product-form.js appelle this.props.addProduct(props) lorsqu'un utilisateur soumet le formulaire. Lorsque le serveur renvoie un succès, l'utilisateur est dirigé 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 effectuer une redirection vers la page Panier depuis la fonction React Router v4?

211
Chris

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 package d’historique :

// src/history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory();

Enveloppez-le ensuite dans <Router> (veuillez noter, vous devriez 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 position actuelle depuis 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 dans React Router FAQ .

143
Oleg Belostotsky

React Router v4 est fondamentalement différent de v3 (et des versions antérieures) et vous ne pouvez plus utiliser browserHistory.Push() comme auparavant.

_ { Cette discussion } _ semble lié si vous voulez plus d'informations:

  • La création d'une nouvelle browserHistory ne fonctionnera pas car <BrowserRouter> crée sa propre instance d'historique et écoute les modifications qui y sont apportées. Donc, une instance différente changera l’URL mais ne mettra pas à jour le <BrowserRouter>.
  • browserHistory n'est pas exposé par react-router dans la v4, uniquement dans la v2.

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

  • Utiliser le composant d'ordre supérieur withRouter

    Au lieu de cela, vous devez utiliser le composant d'ordre supérieur withRouter et le renvoyer au composant qui transmettra à 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);
    

    Consultez le documentation officielle pour plus d'informations:

    Vous pouvez accéder aux propriétés de l’objet history et au plus proche <Route>match via le composant d’ordre supérieur withRouter. withRouter rendra à nouveau son composant à chaque changement d'itinéraire avec les mêmes propriétés que <Route> render props: { match, location, history }.


  • Utiliser l'API context

    Utiliser le contexte peut être l’une des solutions les plus simples, mais étant une API expérimentale, elle est instable et non prise en charge. Utilisez-le uniquement lorsque 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");
      }
      ...
    }
    

    Regardez le documentation officielle sur le contexte:

    Si vous voulez que votre application soit stable, n'utilisez pas le contexte. Il s’agit d’une API expérimentale qui risque de ne plus fonctionner dans les futures versions de React.

    Si vous insistez pour utiliser le contexte malgré ces avertissements, essayez d'isoler votre utilisation du contexte dans une petite zone et évitez d'utiliser l'API de contexte directement lorsque cela est possible, afin de faciliter la mise à niveau lorsque l'API change.

252
Chris

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 du panier. Il sera enregistré dans un objet historique.

Profitez-en, Michael.

21
Michael Horojanski

Mauvaise question, ça m'a pris pas mal de temps, mais finalement, je l'ai résolu de cette façon:

Enveloppez votre conteneur avec withRouter et transmettez l'historique à votre action dans la fonction mapDispatchToProps. En action, utilisez history.Push ('/ url') pour naviguer. 

Action:

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

Récipient: 

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 React Router v4.x .

15
Khrystyna Skvarok

this.context.history.Push ne fonctionnera pas.

J'ai réussi à faire fonctionner Push de la manière suivante:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

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

}
6
Aimar

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 transmettre 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')
    })
  }
}
5
user3812377

J'offre une solution supplémentaire au cas où cela serait utile pour quelqu'un d'autre.

J'ai un fichier history.js où j'ai les éléments suivants:

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 les éléments suivants:

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 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 des appels d'API.

J'espère que ça aide!

4
Diego

Utiliser le rappel. Cela a fonctionné 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();
  });
}

En composant, il vous suffit d'ajouter le rappel

this.props.addProduct(props, () => this.props.history.Push('/cart'))
3
Priyansh Rastogi

Si vous utilisez Redux, je vous recommande d'utiliser le paquet npm react-router-redux Il vous permet de répartir les actions de navigation du magasin Redux.

Vous devez créer le magasin comme décrit dans le 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:

Récipient:

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
Black

Le routeur React V4 permet désormais d’utiliser l’historique comme suit:

this.props.history.Push("/dummy",value)

La valeur peut alors être consultée partout où l'emplacement prop est disponible sous la forme state:{value} pas de composant.

1
Snivio

Voici mon hack (c'est mon fichier de niveau racine, avec un peu de redux mélangé dedans - 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() n'importe où (par exemple, dans les fonctions de mon magasin redux/thunks/sagas, etc.) J'espérais pouvoir utiliser window.customHistory.Push() mais, pour une raison quelconque, react-router n'a jamais semblé être mis à jour, même si l'URL a changé. Mais de cette façon, j'ai l'instance EXACT react-router utilise. Je n'aime pas mettre des choses dans le champ global, 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 à l'OMI.

1
Joao
/*Step 1*/
myFunction(){  this.props.history.Push("/home"); }
/**/
 <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
 Home</button>
0
David Bagdasaryan

vous pouvez l'utiliser comme cela que je le fais pour la connexion 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
jadlmir

ainsi, je le fais comme suit: - au lieu de rediriger avec history.Push, je n'utilise que le composant Redirect à partir de react-router-dom Lorsque vous utilisez ce composant, vous pouvez simplement passer Push=true et il se chargera du reste

import * as React from 'react';
import { Redirect } from 'react-router-dom';
class Example extends React.Component {
  componentDidMount() {
    this.setState({
      redirectTo: '/test/path'
    });
  }

  render() {
    const { redirectTo } = this.state;

    return <Redirect to={{pathname: redirectTo}} Push={true}/>
  }
}
0
LuizAsFight

première étape pour envelopper 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 Route, puis je transmets ces accessoires. Probablement dans l'un de vos fichiers principaux. 

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 console.log (this.props) dans mon fichier composant js, je devrais obtenir quelque chose qui ressemble à ceci

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

Étape 2 Je peux accéder à l'objet d'historique pour changer d'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 bootcamp de codage, donc je ne suis pas un expert, mais je sais que vous pouvez aussi vous en servir 

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

Corrigez-moi si je me trompe, mais lorsque j'ai testé cela, la page entière a été rechargée, ce qui, à mon avis, a complètement annulé l'utilisation de React. 

0
user2785628

Créez une Router personnalisée avec sa propre browserHistory:

import React from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';

export const history = createBrowserHistory();

const ExtBrowserRouter = ({children}) => (
  <Router history={history} >
  { children }
  </Router>
);

export default ExtBrowserRouter

Ensuite, sur votre racine où vous définissez votre Router, utilisez ce qui suit:

import React from 'react';       
import { /*BrowserRouter,*/ Route, Switch, Redirect } from 'react-router-dom';

//Use 'ExtBrowserRouter' instead of 'BrowserRouter'
import ExtBrowserRouter from './ExtBrowserRouter'; 
...

export default class Root extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <ExtBrowserRouter>
          <Switch>
            ...
            <Route path="/login" component={Login}  />
            ...
          </Switch>
        </ExtBrowserRouter>
      </Provider>
    )
  }
}

Enfin, importez history où vous en avez besoin et utilisez-le:

import { history } from '../routers/ExtBrowserRouter';
...

export function logout(){
  clearTokens();      
  history.Push('/login'); //WORKS AS EXPECTED!
  return Promise.reject('Refresh token has expired');
}
0
Nolesh