React-router: Comment invoquer manuellement Link?

131

Je suis nouveau sur ReactJS et sur React-Router. J'ai un composant qui reçoit via des accessoires un <Link/>objet de react-router . Chaque fois que l'utilisateur clique sur un bouton «suivant» dans ce composant, je souhaite appeler l' <Link/>objet manuellement.

À l'heure actuelle, j'utilise des références pour accéder à l' instance de support et en cliquant manuellement sur la balise «a» qui <Link/>génère.

Question: Existe - t-il un moyen d'invoquer manuellement le lien (par exemple this.props.next.go)?

Voici le code actuel que j'ai:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   _onClickNext: function() {
      var next = this.refs.next.getDOMNode();
      next.querySelectorAll('a').item(0).click(); //this sounds like hack to me
   },
   render: function() {
      return (
         ...
         <div ref="next">{this.props.next} <img src="rightArrow.png" onClick={this._onClickNext}/></div>
         ...
      );
   }
});
...

Voici le code que j'aimerais avoir:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   render: function() {
      return (
         ...
         <div onClick={this.props.next.go}>{this.props.next.label} <img src="rightArrow.png" /> </div>
         ...
      );
   }
});
...
Alan Souza
la source

Réponses:

199

React Router v4 - Composant de redirection (mis à jour 2017/04/15)

La méthode recommandée pour la v4 est de permettre à votre méthode de rendu d'attraper une redirection. Utilisez l'état ou les accessoires pour déterminer si le composant de redirection doit être affiché (ce qui déclenche alors une redirection).

import { Redirect } from 'react-router';

// ... your class implementation

handleOnClick = () => {
  // some action...
  // then redirect
  this.setState({redirect: true});
}

render() {
  if (this.state.redirect) {
    return <Redirect push to="/sample" />;
  }

  return <button onClick={this.handleOnClick} type="button">Button</button>;
}

Référence: https://reacttraining.com/react-router/web/api/Redirect

React Router v4 - Contexte du routeur de référence

Vous pouvez également profiter du Routercontexte de 's exposé au composant React.

static contextTypes = {
  router: PropTypes.shape({
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
      replace: PropTypes.func.isRequired
    }).isRequired,
    staticContext: PropTypes.object
  }).isRequired
};

handleOnClick = () => {
  this.context.router.push('/sample');
}

Voilà comment <Redirect />fonctionne sous le capot.

Référence: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Redirect.js#L46,L60

React Router v4 - Objet d'historique de mutation externe

Si vous avez encore besoin de faire quelque chose de similaire à l'implémentation de la v2, vous pouvez créer une copie de BrowserRouterpuis l'exposer en historytant que constante exportable. Voici un exemple de base mais vous pouvez le composer pour l'injecter avec des accessoires personnalisables si nécessaire. Il y a des mises en garde notées avec les cycles de vie, mais il devrait toujours renvoyer le routeur, tout comme dans la v2. Cela peut être utile pour les redirections après une demande d'API d'une fonction d'action.

// browser router file...
import createHistory from 'history/createBrowserHistory';
import { Router } from 'react-router';

export const history = createHistory();

export default class BrowserRouter extends Component {
  render() {
    return <Router history={history} children={this.props.children} />
  }
}

// your main file...
import BrowserRouter from './relative/path/to/BrowserRouter';
import { render } from 'react-dom';

render(
  <BrowserRouter>
    <App/>
  </BrowserRouter>
);

// some file... where you don't have React instance references
import { history } from './relative/path/to/BrowserRouter';

history.push('/sample');

Dernier BrowserRouterà étendre: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js

React Router v2

Poussez un nouvel état sur l' browserHistoryinstance:

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

Référence: https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md

Matt Lo
la source
7
hashHistory.push ('/ échantillon'); si vous utilisez hashHistory au lieu de
browserHistory
1
ceci est particulièrement utile dans la bibliothèque material-ui car l'utilisation de containerElement = {<Link to = "/" />} n'invoque pas toujours le lien
Vishal Disawar
2
Notez qu'avec l'option de redirection, vous devez spécifier push (ie <Redirect push />). Par défaut, il fera un remplacement qui n'est pas du tout la même chose que l'appel manuel d'un lien
aw04
1
@jokab, vous pouvez utiliser <NavLink /> au lieu de <Link /> github.com/ReactTraining/react-router/blob/master/packages/…
Matt Lo
1
La redirection ne fonctionne pas pour moi, mais la solution aw04 avec withRouter est plus simple et plus fonctionnelle
Stackdave
90

React Router 4 inclut un withRouter HOC qui vous donne accès à l' historyobjet via this.props:

import React, {Component} from 'react'
import {withRouter} from 'react-router-dom'

class Foo extends Component {
  constructor(props) {
    super(props)

    this.goHome = this.goHome.bind(this)
  }

  goHome() {
    this.props.history.push('/')
  }

  render() {
    <div className="foo">
      <button onClick={this.goHome} />
    </div>
  }
}

export default withRouter(Foo)
aw04
la source
9
Cela a fonctionné pour moi et cela ressemble à la solution la plus simple.
Rubycut
5
C'est la meilleure solution. Je ne comprends pas pourquoi il a si peu de voix.
Benoit
1
oui, vous pouvez cliquer sur le lien plusieurs fois et le navigateur ne fonctionnera pas. vous devrez cliquer sur le navigateur en arrière plusieurs fois pour vraiment revenir en arrière
Vladyslav Tereshyn
@VladyslavTereshyn, vous pouvez ajouter une logique conditionnelle: if ((this.props.location.pathname + this.props.location.search)! == navigateToPath) {...}
MattWeiler
15

Dans la version 5.x , vous pouvez utiliser le useHistoryhook de react-router-dom:

// Sample extracted from https://reacttraining.com/react-router/core/api/Hooks/usehistory
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>
  );
}
Paulo Mateus
la source
C'est la meilleure solution. Si vous ajoutez une logique conditionnelle, vous pouvez éviter les entrées en double dans l'historique lorsqu'un utilisateur clique plusieurs fois sur le même bouton:if ((routerHistory.location.pathname + routerHistory.location.search) !== navigateToPath) { routerHistory.push(navigateToPath); }
MattWeiler
J'avais besoin de déclarer une historyvariable, l'invocation de répertoire useHistory().pushn'est pas autorisée par les règles de hooks
onmyway133
cela semble la solution de réaction la plus moderne.
Youngjae
8

https://github.com/rackt/react-router/blob/bf89168acb30b6dc9b0244360bcbac5081cf6b38/examples/transitions/app.js#L50

ou vous pouvez même essayer d'exécuter surCliquez sur ceci (solution plus violente):

window.location.assign("/sample");
grechut
la source
À mesure que les lignes de code changent, votre réponse sera meilleure si vous copiez les détails et expliquez votre réponse ici. Ce assignn'est pas non plus une propriété, c'est une fonction.
WiredPrairie
(Mais vous n'avez toujours qu'un lien vers une ligne spécifique d'un fichier). Veuillez inclure la suggestion spécifique dans votre réponse, et pas seulement un lien.
WiredPrairie
Merci pour votre réponse @grechut. Cependant, je veux m'assurer que Document ne sait rien du tout sur le routeur. Le comportement que j'attends est: "Si l'utilisateur clique dans la flèche droite, invoquez la fonction suivante". La fonction suivante peut être un lien ou non.
Alan Souza
J'ai quelques pages gérées en dehors de React (écrans de connexion avec des redirections FB et Google) donc j'en avais besoin dans la navigation pour ces pages puisque "browserHistory.push ('/ home');" seulement changé l'URL, il n'a pas pu acheminer les pages. Je vous remercie.
Deborah
7
Cela rechargerait la page @grechut, ce n'est pas un comportement souhaité pour une application avec un routeur de réaction.
Abhas
2

Ok, je pense que j'ai pu trouver une solution appropriée pour cela.

Maintenant, au lieu d'envoyer <Link/>comme accessoire à Document, j'envoie <NextLink/>un wrapper personnalisé pour le lien react-router. En faisant cela, je suis capable d'avoir la flèche droite dans le cadre de la structure Link tout en évitant toujours d'avoir le code de routage à l'intérieur de l'objet Document.

Le code mis à jour ressemble à ceci:

//in NextLink.js
var React = require('react');
var Right = require('./Right');

var NextLink = React.createClass({
    propTypes: {
        link: React.PropTypes.node.isRequired
    },

    contextTypes: {
        transitionTo: React.PropTypes.func.isRequired
    },

    _onClickRight: function() {
        this.context.transitionTo(this.props.link.props.to);
    },

    render: function() {
        return (
            <div>
                {this.props.link}
                <Right onClick={this._onClickRight} />
            </div>  
        );
    }
});

module.exports = NextLink;

...
//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
var nextLink = <NextLink link={sampleLink} />
<Document next={nextLink} />

//in Document.js
...
var Document = React.createClass({
   render: function() {
      return (
         ...
         <div>{this.props.next}</div>
         ...
      );
   }
});
...

PS : Si vous utilisez la dernière version de react-router, vous devrez peut-être utiliser à la this.context.router.transitionToplace de this.context.transitionTo. Ce code fonctionnera correctement pour la version 0.12.X de react-router.

Alan Souza
la source
2

React Router 4

Vous pouvez facilement invoquer la méthode push via le contexte dans la v4:

this.context.router.push(this.props.exitPath);

où le contexte est:

static contextTypes = {
    router: React.PropTypes.object,
};
Chris
la source
En utilisant BrowserRouter, l'objet de contexte de mes composants ne contient pas d' routerobjet. Est-ce que je fais quelque chose de mal?
pilau
Définissez-vous le contexte dans le composant (le deuxième bloc ci-dessus)?
Chris
Merci d'avoir répondu! finalement cela a fonctionné pour moi: router: React.PropTypes.object.isRequired. Je ne sais pas pourquoi cela n'a pas fonctionné sans la isRequiredclé. Aussi, <Link>semble être en mesure d'obtenir le historycontexte, mais je n'ai pas pu le reproduire.
pilau
Intéressant - si vous mettez en place un codepen, je pourrais vous aider à le déboguer si vous êtes toujours bloqué
Chris
Il semble que vous puissiez l'utiliser this.props.history.push()dans React Router v4. Je n'ai trouvé cela qu'en inspectant les accessoires dans lesquels React Router passe. Cela semble fonctionner mais je ne suis pas sûr que ce soit une bonne idée.
sean_j_roberts
-1

encore une fois c'est JS :) cela fonctionne toujours ....

var linkToClick = document.getElementById('something');
linkToClick.click();

<Link id="something" to={/somewhaere}> the link </Link>
taggartJ
la source