Naviguer par programmation en utilisant le routeur de réaction V4

Je viens de remplacer react-router de v3 à v4.
Mais je ne sais pas comment naviguer par programmation dans la fonction membre d’un Component . c’est-à-dire dans la fonction handleClick() , je veux naviguer vers /path/some/where après avoir traité certaines données. Je faisais ça par:

 import { browserHistory } from 'react-router' browserHistory.push('/path/some/where') 

Mais je ne trouve pas de telles interfaces dans v4.
Comment puis-je naviguer en utilisant v4?

    Si vous ciblez des environnements de navigateur, vous devez utiliser le package react-router-dom , au lieu de react-router . Ils suivent la même approche que React, afin de séparer le code spécifique ( react ) et le code spécifique à la plate-forme ( react-dom , react-native ) avec la différence subtile qu’il n’est pas nécessaire d’installer deux packages distincts. , les packages d’environnement contiennent tout ce dont vous avez besoin. Vous pouvez l’append à votre projet en tant que:

    yarn add react-router-dom

    ou

    npm i -S react-router-dom

    La première chose à faire est de fournir un tant que composant principal de votre application. utilise l’API HTML5 history et le gère pour vous, vous n’avez donc pas à vous soucier de l’instancier vous-même et de le transmettre au composant tant que prop (comme dans les versions précédentes).

    Dans la version 4, pour naviguer par programme, vous devez accéder à l’object history , qui est disponible via le context React, tant que vous disposez d’un composant fournisseur tant que principal parent de votre application. La bibliothèque expose via le contexte l’object router , qui contient lui-même l’ history tant que propriété. L’interface history propose plusieurs méthodes de navigation, telles que push , replace et goBack , entre autres. Vous pouvez consulter la liste complète des propriétés et des méthodes ici .

    Note importante pour les utilisateurs Redux / Mobx

    Si vous utilisez redux ou mobx comme bibliothèque de gestion d’état dans votre application, vous avez peut-être rencontré des problèmes avec les composants sensibles à l’emplacement qui ne sont pas restitués après le déclenchement d’une mise à jour d’URL.

    Cela se produit parce que react-router transmet l’ location aux composants en utilisant le modèle de contexte.

    Connect et Observ créent des composants dont les méthodes shouldComponentUpdate effectuent une comparaison superficielle de leurs accessoires actuels et de leurs prochains accessoires. Ces composants ne seront rendus que si au moins un accessoire a changé. Cela signifie que, pour s’assurer qu’ils se mettent à jour lorsque l’emplacement change, ils doivent recevoir un accessoire qui change lorsque l’emplacement change.

    Les 2 approches pour résoudre ce problème sont:

    • Enveloppez votre composant connecté dans un . L’object de location actuel est l’un des accessoires qu’un transmet au composant qu’il affiche
    • Enveloppez votre composant connecté avec le composant d’ withRouter supérieur withRouter , qui a en fait le même effet et injecte l’ location comme un accessoire

    Mis à part cela, il existe quatre façons de naviguer par programme, classées par recommandation:

    1.- Utiliser un composant

    Il favorise un style déclaratif. Avant la v4, les composants étaient placés en haut de la hiérarchie de vos composants, et devaient réfléchir à la structure de vos routes au préalable. Cependant, vous pouvez maintenant avoir des composants n’importe où dans votre arborescence, ce qui vous permet d’avoir un contrôle plus fin pour un rendu conditionnel en fonction de l’URL. Route injecte la match , l’ location et l’ history tant que supports dans votre composant. Les méthodes de navigation (telles que push , replace , goBack …) sont disponibles en tant que propriétés de l’object history .

    Il y a 3 façons de rendre quelque chose avec une Route , en utilisant un component , un render ou children accessoires children , mais n’utilisez pas plus d’un dans la même Route . Le choix dépend du cas d’utilisation, mais fondamentalement, les deux premières options ne rendront votre composant que si le path correspond à l’emplacement de l’URL, tandis que pour les children le composant sera rendu, qu’il corresponde ou non à l’emplacement. sur l’URL correspondant).

    Si vous souhaitez personnaliser la sortie du rendu de vos composants , vous devez envelopper votre composant dans une fonction et utiliser l’option de render , afin de transmettre à votre composant les autres accessoires de votre choix, en dehors de la match , de l’ location et de l’ history . Un exemple pour illustrer:

     import { BrowserRouter as Router } from 'react-router-dom' const ButtonToNavigate = ({ title, history }) => (  ); const SomeComponent = () => (  } /> ) const App = () => (   // Notice how in v4 we can have any other component interleaved   ); 

    2.- Utiliser withRouter HoC

    Ce composant d’ordre supérieur injectera les mêmes accessoires que Route . Cependant, cela implique que vous ne pouvez avoir qu’un seul HoC par fichier.

     import { withRouter } from 'react-router-dom' const ButtonToNavigate = ({ history }) => (  ); ButtonToNavigate.propTypes = { history: React.PropTypes.shape({ push: React.PropTypes.func.isRequired, }), }; export default withRouter(ButtonToNavigate); 

    3.- Utilisation d’un composant de Redirect

    Le rendu d’une permet d’accéder à un nouvel emplacement. Mais gardez à l’esprit que, par défaut , l’emplacement actuel est remplacé par le nouveau, comme les redirections côté serveur (HTTP 3xx). Le nouvel emplacement est fourni par prop, qui peut être une chaîne (URL à redirect) ou un object de location . Si vous souhaitez plutôt insérer une nouvelle entrée dans l’histoire , passez également un propulseur et réglez-le sur true

      

    4.- Accéder au router manuellement via le contexte

    Un peu découragé car le contexte est toujours une API expérimentale et il est susceptible de se casser / changer dans les futures versions de React

     const ButtonToNavigate = (props, context) => (  ); ButtonToNavigate.contextTypes = { router: React.PropTypes.shape({ history: React.PropTypes.object.isRequired, }), }; 

    Inutile de dire qu’il existe également d’autres composants de routeur destinés à des écosystèmes autres que les navigateurs, tels que qui réplique une stack de navigation dans la mémoire et cible la plate-forme native React, disponible via le package react-router-native .

    Pour toute référence ultérieure, n’hésitez pas à consulter les documents officiels . Il y a aussi une vidéo réalisée par l’un des co-auteurs de la bibliothèque, qui fournit une introduction plutôt intéressante sur react-router v4, en soulignant certains des changements majeurs.

    Le moyen le plus simple de le faire:

    this.props.history.push("/new/url")

    Remarque:

    • Vous souhaiterez peut-être transmettre l’ history prop du composant parent au composant que vous souhaitez appeler l’action si ce dernier n’est pas disponible.

    J’ai eu un problème similaire lors de la migration vers React-Router v4, alors je vais essayer d’expliquer ma solution ci-dessous.

    S’il vous plait ne considérez pas cette réponse comme le bon moyen de résoudre le problème, j’imagine qu’il y a une bonne chance que React Router v4 devienne plus mature et laisse bêta (il existe peut-être déjà et je ne l’ai pas découvert) .

    Pour le contexte, j’ai eu ce problème car j’utilise occasionnellement Redux-Saga pour modifier par programme l’object d’historique (par exemple, lorsqu’un utilisateur s’authentifie avec succès).

    Dans les documents React Router, examinez le composant et vous pouvez voir que vous avez la possibilité de transmettre votre propre object d’historique via un accessoire. C’est l’essence de la solution – nous fournissons l’object d’historique à React-Router partir d’un module global .

    Pas:

    1. Installer l’historique du module npm – yarn add history ou npm install history --save
    2. créer un fichier appelé history.js dans votre dossier de niveau App.js (c’était ma préférence)

       // src/history.js import createHistory from 'history/createBrowserHistory'; export default createHistory();` 
    3. Ajoutez cet object d’historique à votre composant routeur comme

       // src/App.js import history from '../your/path/to/history.js;'  // Route tags here  
    4. Ajustez l’URL comme auparavant en important votre object d’historique global:

       import history from '../your/path/to/history.js;' history.push('new/path/here/'); 

    Tout devrait restr synchronisé maintenant et vous avez également access à un moyen de définir l’object de l’historique par programme et non via un composant / conteneur.

    TL; DR:

     if (navigate) { return  } 

    La réponse simple et déclarative est que vous devez utiliser en combinaison avec setState()

    push: boolean – à la valeur true, la redirection poussera une nouvelle entrée sur l’historique au lieu de remplacer celle en cours.


     import { Redirect } from 'react-router' class FooBar extends React.Component { state = { navigate: false } render() { const { navigate } = this.state // here is the important part if (navigate) { return  } // ^^^^^^^^^^^^^^^^^^^^^^^ return ( 
    ) } }

    Exemple complet ici . Lire plus ici .

    PS L’exemple utilise les initialiseurs de propriétés ES7 + pour initialiser l’état. Regardez aussi si vous êtes intéressé.

    peut aussi simplement utiliser des accessoires: this.props.history.push (‘new_url’)

    Cela marche:

     import { withRouter } from 'react-router-dom'; const SomeComponent = withRouter(({ history }) => ( 
    history.push('/path/some/where')}> some clickable element
    ); ); export default SomeComponent;

    Ma réponse est similaire à celle d’ Alex . Je ne suis pas sûr de savoir pourquoi React-Router l’a rendu si compliqué. Pourquoi devrais-je mettre mon composant dans un HoC juste pour avoir access à ce qui est essentiellement un global?

    Quoi qu’il en soit, si vous regardez comment ils ont implémenté , ce n’est qu’un petit tour d’ .

    Nous pouvons extraire cette histoire pour pouvoir l’importer de n’importe où. L’astuce, cependant, est que si vous effectuez un rendu côté serveur et que vous essayez d’ import le module d’historique, cela ne fonctionnera pas car il utilise des API de navigateur uniquement. Mais ce n’est pas grave car nous ne redirigeons généralement qu’en réponse à un clic ou à un autre événement côté client. Ainsi, il est probablement correct de faire semblant:

     // history.js if(__SERVER__) { module.exports = {}; } else { module.exports = require('history').createBrowserHistory(); } 

    Avec l’aide de webpack, nous pouvons définir des vars afin de savoir dans quel environnement nous nous trouvons:

     plugins: [ new DefinePlugin({ '__SERVER__': 'false', '__BROWSER__': 'true', // you really only need one of these, but I like to have both }), 

    Et maintenant tu peux

     import history from './history'; 

    De partout. Il ne vous restra plus qu’à retourner un module vide sur le serveur.

    Si vous ne voulez pas utiliser ces vars magiques, vous devrez simplement require dans l’object global où il est nécessaire (dans votre gestionnaire d’événements). import ne fonctionnera pas car elle ne fonctionne qu’au niveau supérieur.

    Étape 1: Il n’y a qu’une seule chose à importer en haut:

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

    Étape 2: Dans votre itinéraire, passez l’histoire:

       (  .)}/> 

    Étape 3: L’historique est accepté comme élément des accessoires dans le prochain composant, vous pouvez donc simplement:

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

    C’était facile et vraiment puissant.

    Je teste la v4 depuis quelques jours et je l’aime tellement! Cela prend tout son sens après un certain temps.

    J’ai aussi eu la même question et j’ai trouvé que la manipulation comme les suivantes fonctionnait mieux (et pourrait même être comme prévu). Il utilise l’état, un opérateur ternaire et .

    Dans le constructeur ()

     this.state = { redirectTo: null } this.clickhandler = this.clickhandler.bind(this); 

    Dans le rendu ()

     render(){ return ( 
    { this.state.redirectTo ? : (
    .. ..
    ) }

    Dans le clickhandler ()

      this.setState({ redirectTo: '/path/some/where' }); 

    J’espère que cela aide. Faites le moi savoir.

    Comme il n’y a pas d’autre moyen de gérer cette horrible conception, j’ai écrit un composant générique qui utilise l’approche HOC de withRouter . L’exemple ci-dessous encapsule un élément de button , mais vous pouvez choisir n’importe quel élément cliquable dont vous avez besoin:

     import React from 'react'; import PropTypes from 'prop-types'; import { withRouter } from 'react-router-dom'; const NavButton = (props) => (  ); NavButton.propTypes = { history: PropTypes.shape({ push: PropTypes.func.isRequired }), to: PropTypes.ssortingng.isRequired }; export default withRouter(NavButton); 

    Usage:

     Click me 

    Comme parfois je préfère changer de route par Application puis par boutons, c’est un exemple de travail minimal qui fonctionne pour moi:

     import { Component } from 'react' import { BrowserRouter as Router, Link } from 'react-router-dom' class App extends Component { constructor(props) { super(props) /** @type BrowserRouter */ this.router = undefined } async handleSignFormSubmit() { await magic() this.router.history.push('/') } render() { return (  this.router = el }> Sign in  (  )} />  ) } } 

    J’ai lutté avec cela pendant un moment – quelque chose de si simple, mais si compliqué, parce que ReactJS est juste une façon complètement différente d’écrire des applications Web, c’est très étranger à nous, les anciens!

    J’ai créé un composant distinct pour résumer le désordre:

     // LinkButton.js import React from "react"; import PropTypes from "prop-types"; import {Route} from 'react-router-dom'; export default class LinkButton extends React.Component { render() { return (  (  )}/> ); } } LinkButton.propTypes = { to: PropTypes.ssortingng.isRequired }; 

    Ensuite, ajoutez-le à votre méthode render() :

      Button Text  

    Je pense que @rgommezz couvre la plupart des cas moins un que je trouve très important.

     // history is already a dependency or React Router, but if don't have it then try npm install save-dev history import createHistory from "history/createBrowserHistory" // in your function then call add the below const history = createHistory(); // Use push, replace, and go to navigate around. history.push("/home"); 

    Cela me permet d’écrire un service simple avec des actions / appels que je peux appeler pour faire la navigation à partir de n’importe quel composant que je veux sans faire beaucoup de HoC sur mes composants …

    Il n’est pas clair pourquoi personne n’a fourni cette solution auparavant. J’espère que cela vous aidera, et si vous voyez un problème, merci de me le faire savoir.