Naviguer par programme à l'aide du routeur React

1431

Avec react-routerje peux utiliser l' Linkélément pour créer des liens qui sont gérés nativement par le routeur React.

Je vois en interne que ça appelle this.context.transitionTo(...).

Je veux faire une navigation. Pas à partir d'un lien, mais à partir d'une sélection déroulante (à titre d'exemple). Comment puis-je faire cela dans le code? Qu'est-ce que c'est this.context?

J'ai vu le Navigationmixin, mais puis-je m'en passer mixins?

George Mauer
la source
26
@SergioTapia gardez à l'esprit que cette question chevauche une version majeure de react-router, une version majeure de React, ET une version majeure de javascript
George Mauer
5
Voici un lien vers le tutoriel dans les documents officiels de react
chitzui
4
Vous pouvez vérifier cette réponse stackoverflow.com/questions/44127739/…
Shubham Khatri
4
Je suis content que vous ayez obtenu cette réponse, mais je suis triste que cela doive être aussi compliqué. La navigation dans une application Web doit être facile et bien documentée. VueJS le rend trivial. Chaque composant obtient l'instance de routeur injectée automatiquement. Il y a une section clairement définie pour cela dans leurs documents. router.vuejs.org/guide/essentials/navigation.html J'aime React pour beaucoup de choses, mais c'est parfois trop compliqué. </rant>
colefner
7
@GeorgeMauer Vous avez raison. Ce n'est pas le but de cette communauté. Il s'agit de répondre à des questions, pas de se chamailler sur les cadres. Merci pour le rappel.
colefner

Réponses:

1438

React Router v5.1.0 avec crochets

Il y a un nouveau useHistorycrochet dans React Router> 5.1.0 si vous utilisez React> 16.8.0 et des composants fonctionnels.

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

function HomeButton() {
  const history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

React Router v4

Avec la v4 de React Router, il existe trois approches que vous pouvez adopter pour le routage programmatique au sein des composants.

  1. Utilisez le withRoutercomposant d'ordre supérieur.
  2. Utiliser la composition et rendre un <Route>
  3. Utilisez le context.

React Router est principalement un wrapper autour de la historybibliothèque. historygère l'interaction avec le navigateur window.historypour vous avec son navigateur et ses historiques de hachage. Il fournit également un historique de la mémoire qui est utile pour les environnements qui n'ont pas d'historique global. Ceci est particulièrement utile dans le développement d'applications mobiles ( react-native) et les tests unitaires avec Node.

Une historyinstance a deux méthodes de navigation: pushet replace. Si vous considérez le historycomme un tableau d'emplacements visités, pushajoutera un nouvel emplacement au tableau et replaceremplacera l'emplacement actuel dans le tableau par le nouveau. En règle générale, vous souhaiterez utiliser la pushméthode lorsque vous naviguez.

Dans les versions antérieures de React Router, vous deviez créer votre propre historyinstance, mais dans la version 4 <BrowserRouter>, les composants <HashRouter>, et <MemoryRouter>créeront un navigateur, un hachage et des instances de mémoire pour vous. React Router rend les propriétés et les méthodes de l' historyinstance associée à votre routeur disponibles via le contexte, sous l' routerobjet.

1. Utilisez le withRoutercomposant d'ordre supérieur

Le withRoutercomposant d'ordre supérieur injectera l' historyobjet comme un accessoire du composant. Cela vous permet d'accéder aux méthodes pushet replacesans avoir à gérer les context.

import { withRouter } from 'react-router-dom'
// this also works with react-router-native

const Button = withRouter(({ history }) => (
  <button
    type='button'
    onClick={() => { history.push('/new-location') }}
  >
    Click Me!
  </button>
))

2. Utilisez la composition et rendez un <Route>

Le <Route>composant n'est pas seulement pour les emplacements correspondants. Vous pouvez rendre un itinéraire sans chemin et il correspondra toujours à l'emplacement actuel . Le <Route>composant passe les mêmes accessoires que withRouter, vous pourrez donc accéder aux historyméthodes via l' historyhélice.

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

const Button = () => (
  <Route render={({ history}) => (
    <button
      type='button'
      onClick={() => { history.push('/new-location') }}
    >
      Click Me!
    </button>
  )} />
)

3. Utilisez le contexte *

Mais vous ne devriez probablement pas

La dernière option est celle que vous ne devez utiliser que si vous vous sentez à l'aise avec le modèle de contexte de React (l'API de contexte de React est stable à partir de la version 16).

const Button = (props, context) => (
  <button
    type='button'
    onClick={() => {
      // context.history.push === history.push
      context.history.push('/new-location')
    }}
  >
    Click Me!
  </button>
)

// you need to specify the context type so that it
// is available within the component
Button.contextTypes = {
  history: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  })
}

1 et 2 sont les choix les plus simples à implémenter, donc pour la plupart des cas d'utilisation, ce sont vos meilleurs paris.

Paul S
la source
24
J'ai essayé d'utiliser la méthode 1 de cette manière avecRouter (({history}) => {console.log ("hhhhhhhh"); history.push ('/ bets')}); Mais cela n'a jamais fonctionné avec le routeur 4
Dmitry Malugin
57
QUOI!? Je peux juste utiliser withRouterau lieu de passerhistory à travers tous mes composants ?? Gahh, je dois passer plus de temps à lire des documents ...
hellojeffhall
18
Comment pouvez-vous simplement exécuter history.push('/new-location')sans attacher ce comportement à un bouton ou à un autre élément DOM?
Neil
4
erreur de lancement commeUnexpected use of 'history' no-restricted-globals
Pardeep Jain
18
contextn'est plus expérimental à partir de la réaction 16.
trysis
921

React-Router 5.1.0+ Answer (en utilisant des crochets et React> 16.8)

Vous pouvez utiliser le nouveau useHistorycrochet sur les composants fonctionnels et naviguer par programme:

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

function HomeButton() {
  let history = useHistory();
  // use history.push('/some/path') here
};

Réponse de React-Router 4.0.0+

Dans les versions 4.0 et supérieures, utilisez l'historique comme accessoire de votre composant.

class Example extends React.Component {
   // use `this.props.history.push('/some/path')` here
};

REMARQUE: this.props.history n'existe pas dans le cas où votre composant n'a pas été rendu par <Route>. Vous devez utiliser <Route path="..." component={YourComponent}/>pour avoir this.props.history dans YourComponent

Réponse de React-Router 3.0.0+

Dans les versions 3.0 et supérieures, utilisez le routeur comme accessoire de votre composant.

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

React-Router 2.4.0+Réponse de

Dans les versions 2.4 et supérieures, utilisez un composant d'ordre supérieur pour obtenir le routeur comme accessoire de votre composant.

import { withRouter } from 'react-router';

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

// Export the decorated class
var DecoratedExample = withRouter(Example);

// PropTypes
Example.propTypes = {
  router: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  }).isRequired
};

React-Router 2.0.0+Réponse de

Cette version est rétrocompatible avec 1.x, il n'est donc pas nécessaire d'avoir un guide de mise à niveau. Le simple fait de parcourir les exemples devrait suffire.

Cela dit, si vous souhaitez passer au nouveau modèle, il y a un module browserHistory à l'intérieur du routeur auquel vous pouvez accéder avec

import { browserHistory } from 'react-router'

Maintenant, vous avez accès à l'historique de votre navigateur, vous pouvez donc faire des choses comme pousser, remplacer, etc ...

browserHistory.push('/some/path')

Pour en savoir plus: Histoires et navigation


Réponse de React-Router 1.xx

Je n'entrerai pas dans les détails de mise à niveau. Vous pouvez lire à ce sujet dans le Guide de mise à niveau

Le principal changement concernant la question ici est le passage de la navigation mixin à l'historique. Maintenant, il utilise l'historique du navigateurAPI pour changer d'itinéraire, nous allons donc utiliserpushState() partir de maintenant.

Voici un exemple utilisant Mixin:

var Example = React.createClass({
  mixins: [ History ],
  navigateToHelpPage () {
    this.history.pushState(null, `/help`);
  }
})

Notez que cela Historyvient de rackt / history projet . Pas de React-Router lui-même.

Si vous ne voulez pas utiliser Mixin pour une raison quelconque (peut-être à cause de la classe ES6), vous pouvez accéder à l'historique que vous obtenez du routeur this.props.history. Il ne sera accessible que pour les composants rendus par votre routeur. Donc, si vous souhaitez l'utiliser dans des composants enfants, il doit être transmis en tant qu'attribut viaprops .

Vous pouvez en savoir plus sur la nouvelle version sur leur documentation 1.0.x

Voici une page d'aide sur la navigation en dehors de votre composant

Il recommande de saisir une référence history = createHistory()et d'appeler replaceStateà cela.

Réponse de React-Router 0.13.x

J'ai rencontré le même problème et je n'ai trouvé la solution qu'avec le mixage de navigation fourni avec react-router.

Voici comment je l'ai fait

import React from 'react';
import {Navigation} from 'react-router';

let Authentication = React.createClass({
  mixins: [Navigation],

  handleClick(e) {
    e.preventDefault();

    this.transitionTo('/');
  },

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

J'ai pu appeler transitionTo()sans avoir besoin d'accéder.context

Ou vous pouvez essayer le fantaisie ES6 class

import React from 'react';

export default class Authentication extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    e.preventDefault();

    this.context.router.transitionTo('/');
  }

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
}

Authentication.contextTypes = {
  router: React.PropTypes.func.isRequired
};

React-Router-Redux

Remarque: si vous utilisez Redux, il y a un autre projet appelé ReactRouter-Redux qui vous donne Redux pour les liaisons ReactRouter, en utilisant un peu la même approche que React-Redux fait

React-Router-Redux a quelques méthodes disponibles qui permettent une navigation simple depuis les créateurs d'actions. Ceux-ci peuvent être particulièrement utiles pour les personnes qui ont une architecture existante dans React Native, et qui souhaitent utiliser les mêmes modèles dans React Web avec un minimum de surcharge.

Explorez les méthodes suivantes:

  • push(location)
  • replace(location)
  • go(number)
  • goBack()
  • goForward()

Voici un exemple d'utilisation, avec Redux-Thunk :

./actioncreators.js

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

export const onBackPress = () => (dispatch) => dispatch(goBack())

./viewcomponent.js

<button
  disabled={submitting}
  className="cancel_button"
  onClick={(e) => {
    e.preventDefault()
    this.props.onBackPress()
  }}
>
  CANCEL
</button>
Felipe Skinner
la source
3
J'utilise v2.4.0mais l'approche mentionnée ne fonctionne pas pour moi, mon application ne s'affiche pas du tout et les sorties de la console: Uncaught TypeError: (0 , _reactRouter.withRouter) is not a functionvoici le lien vers mon message SO: stackoverflow.com/questions/37306166/…
nburk
7
Cela devrait être la réponse acceptée, celle qui est acceptée est un peu en désordre et est très ancienne. @GiantElk Je le fais fonctionner comme décrit dans la réponse pour la version 2.6.0.
JMac
2
Cette approche est-elle toujours la voie recommandée pour la version 3.0.x? Beaucoup de gens semblent utiliser le contextchemin.
velop
6
en 4.0, this.props.history n'existe pas. Que se passe-t-il?
Nicolas S.Xu du
4
@ NicolasS.Xu this.props.historyn'existe pas dans le cas où votre composant n'a pas été rendu par <Route>. Vous devez utiliser <Route path="..." component={YourComponent}/>d'avoir this.props.historydans YourComponent.
vogdb
508

React-Router v2

Pour la version la plus récente ( v2.0.0-rc5), la méthode de navigation recommandée consiste à pousser directement sur le singleton historique. Vous pouvez le voir en action dans le document Navigation en dehors des composants .

Extrait pertinent:

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

Si vous utilisez la nouvelle API react-router, vous devez utiliser le historyfrom this.propsà l'intérieur des composants afin:

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

Il propose également pushState mais qui est déconseillé par les avertissements enregistrés.

Si vous utilisez react-router-redux, il offre une pushfonction que vous pouvez envoyer comme ceci:

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

Cependant, cela ne peut être utilisé que pour changer l'URL, pas pour naviguer réellement vers la page.

Policier
la source
15
N'oubliez pas que la nouvelle API n'utilise pas import { browserHistory } from './react-router'mais crée à la place l'historique en utilisant import createBrowserHistory from 'history/lib/createBrowserHistory'. Plus tard, vous pouvez accéder à historypartir des accessoires des composants:this.props.history('/some/path')
Ricardo Pedroni
7
var browserHistory = require('react-router').browserHistory; browserHistory.goBack();
Bobby
7
react-router-redux pushne change que l'URL, ne change pas réellement la page. Pour ce faire , à la fois, l' importation browserHistoryd' react-routerutilisation browserHistory.push('/my-cool-path'). Malheureusement, ce n'est pas super facile à trouver. github.com/reactjs/react-router/blob/master/docs/guides/…
Andrew Samuelsen
4
J'utilise this.props.history.push ('/') mais seule l'URL est modifiée ... Pas de routage réel en cours ici. Qu'est-ce que je rate?
rgcalsaverini
2
Je viens de télécharger mon point de vue sur la façon de naviguer par programme dans la dernière react-routerv4
spik3s
56

Voici comment procéder react-router v2.0.0avec ES6 . react-routers'est éloigné des mixins.

import React from 'react';

export default class MyComponent extends React.Component {
  navigateToPage = () => {
    this.context.router.push('/my-route')
  };

  render() {
    return (
      <button onClick={this.navigateToPage}>Go!</button>
    );
  }
}

MyComponent.contextTypes = {
  router: React.PropTypes.object.isRequired
}
Alex Miller
la source
5
Je crois que la recommandation actuelle est d'utiliser le historysingleton comme indiqué par @Bobby. Vous pouvez utiliser context.routermais vous rendant vraiment difficile à tester l' unité de ces composants instanciation simplement ce composant pas cela dans le contexte.
George Mauer
2
@GeorgeMauer Je pense que cela dépend de l'endroit où le code s'exécute. Selon les documents React, l'utilisation de context.router est recommandée si elle est exécutée sur le serveur. github.com/reactjs/react-router/blob/master/upgrade-guides/…
KumarM
50

Réponse de React-Router 4.x:

De mon côté, j'aime avoir un seul objet historique que je peux transporter même en dehors des composants. Ce que j'aime faire, c'est avoir un seul fichier history.js que j'importe à la demande et le manipuler.

Il vous suffit de passer BrowserRouterau routeur et de spécifier l'accessoire d'historique. Cela ne change rien pour vous, sauf que vous avez votre propre objet historique que vous pouvez manipuler comme vous le souhaitez.

Vous devez installer historique , la bibliothèque utilisée par react-router.

Exemple d'utilisation, notation ES6:

history.js

import createBrowserHistory from 'history/createBrowserHistory'
export default createBrowserHistory()

BasicComponent.js

import React, { Component } from 'react';
import history from './history';

class BasicComponent extends Component {

    goToIndex(e){
        e.preventDefault();
        history.push('/');
    }

    render(){
        return <a href="#" onClick={this.goToIndex}>Previous</a>;
    }
}

EDIT du 16 avril 2018:

Si vous devez naviguer à partir d'un composant qui est réellement rendu à partir d'un Route composant, vous pouvez également accéder à l'historique à partir d'accessoires, comme ceci:

BasicComponent.js

import React, { Component } from 'react';

class BasicComponent extends Component {

    navigate(e){
        e.preventDefault();
        this.props.history.push('/url');
    }

    render(){
        return <a href="#" onClick={this.navigate}>Previous</a>;
    }
}
Eric Martin
la source
2
Sauf que les documents React Router nous disent que vous ne devriez pas, dans la plupart des cas, avoir besoin d'utiliser à la Routerplace de BrowserRouter. BrowserRoutercrée et gère l' historyobjet pour vous. Vous ne devriez pas créer par défaut le vôtre, uniquement si vous ["avez besoin d'une intégration approfondie avec des outils de gestion d'état comme Redux."] Reacttraining.com/react-router/web/api/Router
bobbyz
2
C'est quelque chose que j'ai appris au fil du temps et de l'expérience. Bien que ce soit généralement une bonne pratique d'utiliser la valeur par défaut this.props.history, je n'ai pas encore trouvé de solution qui puisse m'aider à faire une telle chose dans une classe qui n'est pas un composant ou tout autre outil qui n'est pas construit en tant que composant React auquel vous pouvez transmettre des accessoires. Merci pour vos commentaires :)
Eric Martin
44

Pour celui-ci, qui ne contrôle pas le côté serveur et à cause de cela utilise le hachage routeur v2:

Placez votre historique dans un fichier séparé (par exemple, app_history.js ES6):

import { useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });

export default appHistory;

Et utilisez-le partout!

Votre point d'entrée pour react-router (app.js ES6):

import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Redirect } from 'react-router'
import appHistory from './app_history'
...
const render((
  <Router history={appHistory}>
  ...
  </Router>
), document.querySelector('[data-role="app"]'));

Votre navigation dans n'importe quel composant (ES6):

import appHistory from '../app_history'
...
ajaxLogin('/login', (err, data) => {
  if (err) {
    console.error(err); // login failed
  } else {
    // logged in
    appHistory.replace('/dashboard'); // or .push() if you don't need .replace()
  }
})
Alexey Volodko
la source
1
Merci. Peut-être que je manque quelque chose. N'est-ce pas exactement la même chose que les deux premières réponses?
George Mauer
2
Lorsque vous utilisez la navigation par "hachage", dans "react-router" v2, vous devez installer un module supplémentaire "historique" (si vous voulez éviter d'étranges requêtes de hachage), et au lieu d'utiliser browserHistory, vous devez utiliser useRouterHistory (createHashHistory) ( {queryKey: false}). C'est la raison principale pour laquelle j'ai posté cela. J'ai montré comment naviguer avec l'historique de hachage.
Alexey Volodko
1
Oh, je vois, tu as raison. Je ne suis plus sur ce projet mais je pense que la façon préférée est d'utiliser historypour l'une ou l'autre approche maintenant
George Mauer
4
pourquoi cette réponse n'est pas la meilleure? pourquoi se lancer dans tous les ennuis avec Context ou HOC ou prototypes d'événements, pourquoi ne pas simplement importer l'historique et utiliser où bon nous semble? juste essayer de comprendre le compromis
storm_buster
@storm_buster il n'y a pas de compromis. Nous avons des modules ES6 et nous devons les utiliser! C'est une vitrine typique de l'utilisation de votre outil (réagissez dans ce cas) pour tout, même pour des choses pour lesquelles vous ne voulez pas vraiment les utiliser.
Capaj
40

React Router V4

tl: dr;

if (navigate) {
  return <Redirect to="/" push={true} />
}

La réponse simple et déclarative est que vous devez utiliser <Redirect to={URL} push={boolean} />en combinaison avecsetState()

push: boolean - lorsque true, la redirection poussera une nouvelle entrée dans l'historique au lieu de remplacer l'actuelle.


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 <Redirect to="/" push={true} />
    }
   // ^^^^^^^^^^^^^^^^^^^^^^^

    return (
      <div>
        <button onClick={() => this.setState({ navigate: true })}>
          Home
        </button>
      </div>
    )
  }
}

Exemple complet ici . En savoir plus ici .

PS. L'exemple utilise ES7 + Property Initializers pour initialiser l'état. Regardez ici aussi, si vous êtes intéressé.

Lyubomir
la source
C'est la meilleure réponse que j'ai trouvée. L'utilisation withRoutern'a pas fonctionné lorsque vous êtes déjà sur l'itinéraire en cours de rechargement. Dans notre cas, nous avons dû sélectivement faire setState(provoquer return <Redirect>) si déjà sur la route ou history.push()d'ailleurs.
karmakaze
Très, très intelligent. Et pourtant très simple. Tks. Je l'ai utilisé dans ReactRouterTable car je voulais gérer le clic sur toute la ligne. Utilisé sur onRowClick pour définir l'état. setState ({naviguer vers: "/ chemin /" + row.uuid});
Lovato du
c'est une réponse à toute épreuve.
Nitin Jadhav
31

Attention: cette réponse ne couvre que les versions ReactRouter antérieures à 1.0

Je mettrai à jour cette réponse avec les cas d'utilisation 1.0.0-rc1 après!

Vous pouvez aussi le faire sans mixins.

let Authentication = React.createClass({
  contextTypes: {
    router: React.PropTypes.func
  },
  handleClick(e) {
    e.preventDefault();
    this.context.router.transitionTo('/');
  },
  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

Le piège avec les contextes est qu'il n'est pas accessible sauf si vous définissez le contextTypes sur la classe.

Quant au contexte, c'est un objet, comme les accessoires, qui est transmis du parent à l'enfant, mais il est transmis implicitement, sans avoir à redéclarer les accessoires à chaque fois. Voir https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html

Qiming
la source
26

J'ai essayé au moins 10 façons de le faire avant que quelque chose fonctionne bien!

La withRouterréponse de @Felipe Skinner était un peu écrasante pour moi, et je n'étais pas sûr de vouloir créer de nouveaux noms de classe "ExportedWithRouter".

Voici la façon la plus simple et la plus propre de le faire, vers React-Router 3.0.0 et ES6 actuels:

React-Router 3.xx avec ES6:

import { withRouter } from 'react-router';

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

// Export the decorated class
export default withRouter(Example);

ou, si ce n'est pas votre classe par défaut, exportez comme:

withRouter(Example);
export { Example };

Notez que dans 3.xx, le <Link>composant lui-même utilise router.push, vous pouvez donc lui transmettre tout ce que vous voulez passer <Link to=, comme:

   this.props.router.push({pathname: '/some/path', query: {key1: 'val1', key2: 'val2'})'
Ben Wheeler
la source
1
Cela fonctionne parfaitement. Mais répond 200 OKau lieu du 30xcode. Comment puis-je résoudre ce problème?
Muhammad Ateeq Azam
1
Pas certain. Cela ne m'est jamais venu à l'esprit car ce n'est pas une demande externe - c'est simplement votre navigateur qui utilise le propre code de la page pour changer ce qui est sur la page. Vous pouvez toujours faire une redirection javascript manuelle si vous préférez.
Ben Wheeler
1
il peut également être utilisé avec @withRouter en tant que décorateur de classe ... va très bien avec mobx
Dan
21

Pour ce faire la navigation par programme, vous devez pousser une nouvelle histoire à la props.history dans votre component, si quelque chose comme cela peut faire le travail pour vous:

//using ES6
import React from 'react';

class App extends React.Component {

  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick(e) {
    e.preventDefault()
    /* Look at here, you can add it here */
    this.props.history.push('/redirected');
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          Redirect!!!
        </button>
      </div>
    )
  }
}

export default App;
Alireza
la source
Veuillez écrire la version de react-router
Ash
19

Pour les composants ES6 + React, la solution suivante a fonctionné pour moi.

J'ai suivi Felippe skinner, mais j'ai ajouté une solution de bout en bout pour aider les débutants comme moi.

Voici les versions que j'ai utilisées:

"react-router": "^ 2.7.0"

"réagir": "^ 15.3.1"

Ci-dessous est mon composant react où j'ai utilisé la navigation programmatique en utilisant react-router:

import React from 'react';

class loginComp extends React.Component {
   constructor( context) {
    super(context);
    this.state = {
      uname: '',
      pwd: ''
    };
  }

  redirectToMainPage(){
        this.context.router.replace('/home');
  }

  render(){
    return <div>
           // skipping html code 
             <button onClick={this.redirectToMainPage.bind(this)}>Redirect</button>
    </div>;
  }
};

 loginComp.contextTypes = {
    router: React.PropTypes.object.isRequired
 }

 module.exports = loginComp;

Voici la configuration de mon routeur:

 import { Router, Route, IndexRedirect, browserHistory } from 'react-router'

 render(<Router history={browserHistory}>
          <Route path='/' component={ParentComp}>
            <IndexRedirect to = "/login"/>
            <Route path='/login' component={LoginComp}/>
            <Route path='/home' component={HomeComp}/>
            <Route path='/repair' component={RepairJobComp} />
            <Route path='/service' component={ServiceJobComp} />
          </Route>
        </Router>, document.getElementById('root'));
Softwareddy
la source
19

Ce n'est peut-être pas la meilleure approche mais ... En utilisant react-router v4, le Typescript suivant pourrait donner une idée pour certains.

Dans le composant rendu ci-dessous, par exemple LoginPage, l' routerobjet est accessible et il suffit d'appeler router.transitionTo('/homepage')pour naviguer.

Le code de navigation a été extrait de .

"react-router": "^4.0.0-2", "react": "^15.3.1",

import Router from 'react-router/BrowserRouter';
import { History } from 'react-history/BrowserHistory';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();

interface MatchWithPropsInterface {
  component: typeof React.Component,
  router: Router,
  history: History,
  exactly?: any,
  pattern: string
}

class MatchWithProps extends React.Component<MatchWithPropsInterface,any> {
  render() {
    return(
      <Match {...this.props} render={(matchProps) => (
             React.createElement(this.props.component, this.props)

        )}
       />
    )
  }
}

ReactDOM.render(
    <Router>
      {({ router }) => (
        <div>
          <MatchWithProps exactly pattern="/" component={LoginPage} router={router} history={history} />
          <MatchWithProps pattern="/login" component={LoginPage} router={router} history={history} />
          <MatchWithProps pattern="/homepage" component={HomePage} router={router} history={history} />
          <Miss component={NotFoundView} />
        </div>
      )}
    </Router>,

   document.getElementById('app')
);

mcku
la source
1
Pourquoi n'utilisez-vous pas avecRouter et browserHistory?
Erwin Mayer
1
@Erwin L'API est différente dans la v4. Voir ce github.com/ReactTraining/react-router/issues/3847 .
mcku
1
@mcku Où avez-vous obtenu votre fichier dts dactylographié pour react-router v4?
jmathew
1
@jmathew Je n'ai aucune définition de type pour
react
16

Dans React-Router v4 et ES6

Vous pouvez utiliser withRouteret this.props.history.push.

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

class Home extends Component {

    componentDidMount() {
        this.props.history.push('/redirect-to');
    }
}

export default withRouter(Home);
Hossein
la source
2
Déjà mentionné dans plusieurs autres réponses (y compris longuement dans la première réponse)
George Mauer
13

Pour l'utiliser withRouteravec un composant basé sur une classe, essayez quelque chose comme ceci ci-dessous. N'oubliez pas de modifier la déclaration d'exportation à utiliser withRouter:

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

class YourClass extends React.Component {
  yourFunction = () => {
    doSomeAsyncAction(() =>
      this.props.history.push('/other_location')
    )
  }

  render() {
    return (
      <div>
        <Form onSubmit={ this.yourFunction } />
      </div>
    )
  }
}

export default withRouter(YourClass);
Janos
la source
11

basé sur la réponse précédente
de José Antonio Postigo et Ben Wheeler
la nouveauté? doit être écrit en tapuscrit
et utiliser des décorateurs
OU une propriété / un champ statique

import * as React from "react";
import Component = React.Component;
import { withRouter } from "react-router";

export interface INavigatorProps {
    router?: ReactRouter.History.History;
}

/**
 * Note: goes great with mobx 
 * @inject("something") @withRouter @observer
 */
@withRouter
export class Navigator extends Component<INavigatorProps, {}>{
    navigate: (to: string) => void;
    constructor(props: INavigatorProps) {
        super(props);
        let self = this;
        this.navigate = (to) => self.props.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

/**
 * Non decorated 
 */
export class Navigator2 extends Component<INavigatorProps, {}> {

    static contextTypes = {
        router: React.PropTypes.object.isRequired,
    };

    navigate: (to: string) => void;
    constructor(props: INavigatorProps, context: any) {
        super(props, context);
        let s = this;
        this.navigate = (to) =>
            s.context.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

avec n'importe quel npm installé aujourd'hui. "react-router": "^ 3.0.0" et
"@ types / react-router": "^ 2.0.41"

Dan
la source
11

avec React-Router v4 à l'horizon, il existe désormais une nouvelle façon de procéder.

import { MemoryRouter, BrowserRouter } from 'react-router';

const navigator = global && global.navigator && global.navigator.userAgent;
const hasWindow = typeof window !== 'undefined';
const isBrowser = typeof navigator !== 'undefined' && navigator.indexOf('Node.js') === -1;
const Router = isBrowser ? BrowserRouter : MemoryRouter;

<Router location="/page-to-go-to"/>

react-lego est un exemple d'application qui montre comment utiliser / mettre à jour react-router et il comprend des exemples de tests fonctionnels qui naviguent dans l'application.

peter.mouland
la source
5
C'est idéal pour naviguer à partir de la fonction de rendu, bien que je me demande comment naviguer à partir de quelque chose comme un hook de cycle de vie ou redux?
Ruben Martinez Jr.,
11

Dans réagir le routeur v4. Je poursuis ces deux voies pour router par programmation.

1. this.props.history.push("/something/something")
2. this.props.history.replace("/something/something")

Numéro deux

Remplace l'entrée actuelle sur la pile d'historique

Pour obtenir l'historique des accessoires, vous devrez peut-être envelopper votre composant avec

withRouter

saiful619945
la source
Merci!! Meilleure réponse
Taha Farooqui
9

Si vous utilisez le hachage ou l'historique du navigateur, vous pouvez le faire

hashHistory.push('/login');
browserHistory.push('/login');
Zaman Afzal
la source
2
hashHistory.push, donne Impossible de lire la propriété "push" de undefined. D'où les importez-vous?
ArchNoob
importer {hashHistory} depuis "react-router"; vous pouvez l'importer de cette façon en haut.
Zaman Afzal
J'utilise votre exemple de code de mon fichier de saga redux, en important comme vous l'avez dit et en l'utilisant, mais je reçois toujours cette TypeError.
ArchNoob
8

Avec la version actuelle de React (15.3), cela this.props.history.push('/location');fonctionnait pour moi, mais cela montrait l'avertissement suivant:

browser.js: 49 Avertissement: [react-router] props.historyet context.historysont obsolètes. Veuillez utiliser context.router.

et je l'ai résolu en utilisant context.routercomme ceci:

import React from 'react';

class MyComponent extends React.Component {

    constructor(props) {
        super(props);
        this.backPressed = this.backPressed.bind(this);
    }

    backPressed() {
        this.context.router.push('/back-location');
    }

    ...
}

MyComponent.contextTypes = {
    router: React.PropTypes.object.isRequired
};

export default MyComponent;
José Antonio Postigo
la source
1
Cela fonctionne pour V4. Eh bien ... fermer ... il doit en fait être this.context.router.history.push ('/ back-location');
velohomme
7

React-Router V4

si vous utilisez la version 4, vous pouvez utiliser ma bibliothèque (plug Shameless) où vous envoyez simplement une action et tout fonctionne!

dispatch(navigateTo("/aboutUs"));

trippler

Garry Taylor
la source
5

Ceux qui rencontrent des problèmes lors de l'implémentation de ceci sur react-router v4.

Voici une solution de travail pour naviguer dans l'application React à partir d'actions redux.

history.js

import createHistory from 'history/createBrowserHistory'

export default createHistory()

App.js / Route.jsx

import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
 <Route path="/test" component={Test}/>
</Router>

autre_fichier.js OU fichier redux

import history from './history' 

history.push('/test') // this should change the url and re-render Test component

Tout cela grâce à ce commentaire: Commentaire sur les problèmes de ReactTraining

reflexgravity
la source
4

S'il arrive de coupler RR4 w / redux via react -router-redux , utilisez également les créateurs d'actions de routage de react-router-redux.

import { push, replace, ... } from 'react-router-redux'

class WrappedComponent extends React.Component {
  handleRedirect(url, replaceState = true) { 
    replaceState 
      ? this.props.dispatch(replace(url)) 
      : this.props.dispatch(push(url)) 
  }
  render() { ... }
}

export default connect(null)(WrappedComponent)

Si vous utilisez redux thunk / saga pour gérer le flux asynchrone, importez les créateurs d'actions ci-dessus dans des actions redux et hook pour faire réagir les composants à l'aide de mapDispatchToProps pourrait être mieux.

Xlee
la source
1
react-router-reduxest obsolète / archivé depuis longtemps
oligofren
4

Vous pouvez également utiliser le useHistorycrochet dans un composant sans état. Exemple de la documentation.

import { useHistory } from "react-router"

function HomeButton() {
  const history = useHistory()

  return (
    <button type="button" onClick={() => history.push("/home")}>
      Go home
    </button>
  )
}

Remarque: des crochets ont été ajoutés [email protected]et nécessitentreact@>=16.8

Nickofthyme
la source
1
Bon appel, pourriez-vous noter à quelle version de react-router et réagir qui fait référence? Il s'agit d'un nouveau changement qui n'était pas toujours disponible
George Mauer
3

La bonne réponse était pour moi au moment de la rédaction

this.context.router.history.push('/');

Mais vous devez ajouter des PropTypes à votre composant

Header.contextTypes = {
  router: PropTypes.object.isRequired
}
export default Header;

N'oubliez pas d'importer des PropTypes

import PropTypes from 'prop-types';
webmaster
la source
Veuillez noter la version de react-router, et qu'avez-vous importé de react-router
Ash
Rien à importer et voici la version "react-router": "^ 4.2.0"
webmaster
3

Ce n'est peut-être pas la meilleure solution, mais cela fait l'affaire:

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

// create functional component Post
export default Post = () => (
    <div className="component post">

        <button className="button delete-post" onClick={() => {
            // ... delete post
            // then redirect, without page reload, by triggering a hidden Link
            document.querySelector('.trigger.go-home').click();
        }}>Delete Post</button>

        <Link to="/" className="trigger go-home hidden"></Link>

    </div>
);

Fondamentalement, une logique liée à une action (dans ce cas, une post-suppression) finira par appeler un déclencheur de redirection. Ce n'est pas idéal car vous ajouterez un «déclencheur» de noeud DOM à votre balisage juste pour pouvoir l'appeler facilement en cas de besoin. De plus, vous interagirez directement avec le DOM, qui, dans un composant React, peut ne pas être souhaité.

Pourtant, ce type de redirection n'est pas souvent nécessaire. Donc, un ou deux liens cachés supplémentaires dans le balisage de votre composant ne feraient pas autant de mal, surtout si vous leur donnez des noms significatifs.

neatsu
la source
Pouvez-vous expliquer pourquoi vous utiliseriez cela sur la solution acceptée?
George Mauer
C'est juste une solution alternative au problème discuté. Comme déjà indiqué, pas idéal mais cela fonctionne très bien pour les redirections programmatiques.
neatsu
2

Cela a fonctionné pour moi, aucune importation spéciale nécessaire:

<input 
  type="button" 
  name="back" 
  id="back" 
  class="btn btn-primary" 
  value="Back" 
  onClick={() => { this.props.history.goBack() }} 
/>
JJ_Coder4Hire
la source
Bien sûr, cela nécessite des importations spéciales - votre composant n'a pas été historyajouté à propslui-même. Cela vient d'une HoC que vous auriez besoin d'importer pour l'utiliser (les composants envoyés directement à par Routesont automatiquement enveloppés par cette HoC mais vous avez besoin d'une importation pour Route...)
George Mauer
2

utilisez plutôt le hookrouter, l'alternative moderne au react-router

https://www.npmjs.com/package/hookrouter

import { useRoutes, usePath, A} from "hookrouter";

Pour répondre à la question OP concernant la liaison via la zone de sélection, vous pouvez le faire:

navigate('/about');
StefanBob
la source
Pour info. L'auteur lui-même qualifie cette bibliothèque de «preuve de concept» et elle est sans activité depuis près d'un an (juin 2019).
MEMark
@MEMark c'est parce que c'est déjà parfait
StefanBob
Eh bien, les 38 problèmes ouverts, ainsi que le fait que la version 2.0 sera une réécriture complète selon l'auteur, disent le contraire.
MEMark
1

Pour React Router v4 +

En supposant que vous n'aurez pas besoin de naviguer pendant le rendu initial lui-même (pour lequel vous pouvez utiliser un <Redirect>composant), c'est ce que nous faisons dans notre application.

Définissez une route vide qui renvoie null, cela vous permettra d'accéder à l'objet historique. Vous devez le faire au niveau supérieur où votreRouter est défini.

Maintenant , vous pouvez faire toutes les choses qui peuvent être faites sur l' histoire comme history.push(), history.replace(), history.go(-1)etc!

import React from 'react';
import { HashRouter, Route } from 'react-router-dom';

let routeHistory = null;

export function navigateTo(path) {
  if(routeHistory !== null) {
    routeHistory.push(path);
  }
}

export default function App(props) {
  return (
    <HashRouter hashType="noslash">
      <Route
        render={({ history }) => {
          routeHistory = history;
          return null;
        }}
      />
      {/* Rest of the App */}
    </HashRouter>
  );
}
Aftab Khan
la source
1

react-router-dom: 5.1.2

  • Ce site comporte 3 pages, toutes affichées dynamiquement dans le navigateur.

  • Bien que la page ne soit jamais actualisée, notez comment React Router maintient l'URL à jour lorsque vous naviguez sur le site. Cela préserve l'historique du navigateur, garantissant que des éléments comme le bouton de retour et les signets fonctionnent correctement.

  • Un commutateur examine tous ses éléments enfants et affiche le premier dont le chemin correspond à l'URL actuelle. Utilisez un moment où vous avez plusieurs itinéraires, mais vous voulez qu'un seul d'entre eux soit rendu à la fois

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";



export default function BasicExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>

        <hr />

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

// You can think of these components as "pages"
// in your app.

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function About() {
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}

function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}```
GOVINDARAJ
la source
Bienvenue chez SO! Lorsque vous répondez à une question avec autant de réponses, essayez de montrer les avantages de la vôtre.
David García Bodego
Cela ne répond pas non plus à la question de savoir comment le faire par programme à partir de js impératifs, pas avec du balisage.
George Mauer
1

Donc, dans ma réponse, il existe 3 façons différentes de rediriger par programme vers une route. Certaines des solutions ont déjà été présentées mais les suivantes se concentrent uniquement sur les composants fonctionnels avec une application de démonstration supplémentaire.

En utilisant les versions suivantes:

réagir: 16.13.1

react -dom: 16.13.1

react-router: 5.2.0

react-router-dom: 5.2.0

dactylographie: 3.7.2

Configuration:

Donc, tout d'abord, la solution utilise HashRouter, configurée comme suit:

<HashRouter>
    // ... buttons for redirect

    <Switch>
      <Route exact path="/(|home)" children={Home} />
      <Route exact path="/usehistory" children={UseHistoryResult} />
      <Route exact path="/withrouter" children={WithRouterResult} />
      <Route exact path="/redirectpush" children={RedirectPushResult} />
      <Route children={Home} />
    </Switch>
</HashRouter>

De la documentation sur <HashRouter>:

Un <Router>qui utilise la partie de hachage de l'URL (c. window.location.hash-à- d. ) Pour garder votre interface utilisateur en synchronisation avec l'URL.

Solutions:

  1. Utiliser <Redirect>pour pousser en utilisant useState:

En utilisant dans un composant fonctionnel ( RedirectPushActioncomposant de mon référentiel), nous pouvons utiliser useStatepour gérer la redirection. La partie délicate est qu'une fois la redirection effectuée, nous devons redirectrétablir l' état false. En utilisant setTimeOutavec 0retard, nous attendons que React s'engageRedirect dans le DOM, puis récupérons le bouton pour l'utiliser la prochaine fois.

Veuillez trouver mon exemple ci-dessous:

const [redirect, setRedirect] = useState(false);
const handleRedirect = useCallback(() => {
    let render = null;
    if (redirect) {
        render = <Redirect to="/redirectpush" push={true} />

        // in order wait until commiting to the DOM
        // and get back the button for clicking next time
        setTimeout(() => setRedirect(false), 0);
    }
    return render;
}, [redirect]);

return <>
    {handleRedirect()}
    <button onClick={() => setRedirect(true)}>
        Redirect push
    </button>
</>

De la <Redirect>documentation:

Le rendu d'un <Redirect>permet d'accéder à un nouvel emplacement. Le nouvel emplacement remplacera l'emplacement actuel dans la pile d'historique, comme le font les redirections côté serveur (HTTP 3xx).

  1. Utilisation du useHistorycrochet:

Dans ma solution, il y a un composant appelé UseHistoryActionqui représente ce qui suit:

let history = useHistory();

return <button onClick={() => { history.push('/usehistory') }}>
    useHistory redirect
</button>

Le useHistorycrochet nous donne accès à l'objet historique qui nous aide à naviguer par programme ou à changer de route.

  1. En utilisant withRouter, obtenir le historyde props:

Créé un composant appelé WithRouterAction, s'affiche comme ci-dessous:

const WithRouterAction = (props:any) => {
    const { history } = props;

    return <button onClick={() => { history.push('/withrouter') }}>
        withRouter redirect
    </button>
}

export default withRouter(WithRouterAction);

Lecture de la withRouterdocumentation:

Vous pouvez accéder aux historypropriétés de l' objet et à la correspondance la plus proche <Route>via le withRoutercomposant d'ordre supérieur. withRouterpassera à jour match, locationet les historyaccessoires au composant encapsulé à chaque rendu.

Démo:

Pour une meilleure représentation, j'ai construit un référentiel GitHub avec ces exemples, veuillez le trouver ci-dessous:

https://github.com/norbitrial/react-router-programmatically-redirect-examples

J'espère que ça aide!

norbitrial
la source