web-dev-qa-db-fra.com

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

Je viens de remplacer react-router de v3 à v4.
Mais je ne suis pas sûr de savoir comment naviguer par programme dans la fonction membre d'une Component. C'est-à-dire dans la fonction handleClick() Je souhaite naviguer vers /path/some/where après le traitement de certaines données ..__ Je le faisais auparavant:

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

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

256
Colin Witkamp

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 avec setState()

Push: boolean - lorsque la valeur est true, la redirection insère une nouvelle entrée dans 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 <Redirect to="/" Push={true} />
    }
   // ^^^^^^^^^^^^^^^^^^^^^^^

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

Exemple complet ici . Lisez plus ici .

PS. L'exemple utilise ES7 + Initializer pour initialiser l'état. Regardez ici aussi, si cela vous intéresse.

32
Lyubomir

Étape 1: Il n’ya qu’une chose à importer en plus: 

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

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

    <Route exact path='/posts/add' render={({history})  => (
      <PostAdd
        history={history}
      />
    .)}/>

Étape 3: l’histoire est acceptée en tant qu’élément des accessoires dans le prochain composant. Vous pouvez donc simplement: 

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

C'était facile et vraiment puissant.

7
cdi-meo

peut aussi simplement utiliser des accessoires: this.props.history.Push('new_url')

6
user1445685

Cela marche:

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

const SomeComponent = withRouter(({ history }) => (
    <div onClick={() => history.Push('/path/some/where')}>
        some clickable element
    </div>); 
);

export default SomeComponent;
6
Kaya Toast

Ma réponse est similaire à Alex's . Je ne suis pas sûr de savoir pourquoi React-Router a compliqué cela inutilement. Pourquoi devrais-je enrober mon composant d'un CdC uniquement pour avoir accès à ce qui est essentiellement mondial? 

Quoi qu'il en soit, si vous regardez comment ils ont implémenté <BrowserRouter> , c'est juste une petite enveloppe autour de history .

Nous pouvons extraire cet historique afin de pouvoir l'importer de n'importe où. Le truc, cependant, est que si vous effectuez un rendu côté serveur et tentez de import le module d'historique, cela ne fonctionnera pas, car il utilise des API uniquement de navigateur. 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 sommes:

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 reste plus qu'à renvoyer un module vide sur le serveur.

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

5
mpen

Je teste la v4 depuis quelques jours maintenant et… je l'aime jusqu'à présent! Cela fait du sens après un moment.

J'avais aussi la même question et j'ai trouvé que le traitement suivant fonctionnait mieux (et pourrait même correspondre à ce à quoi il est destiné). Il utilise state, un opérateur ternaire et <Redirect>.

Dans le constructeur ()

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

Dans le rendu () 

render(){
    return (
        <div>
        { this.state.redirectTo ?
            <Redirect to={{ pathname: this.state.redirectTo }} /> : 
            (
             <div>
               ..
             <button onClick={ this.clickhandler } />
              ..
             </div>
             )
         }

Dans le clickhandler ()

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

J'espère que ça aide. Faites le moi savoir.

4
jar0m1r

Puisqu'il n'y a pas d'autre moyen de gérer ce design horrible, j'ai écrit un composant générique qui utilise l'approche withRouterHOC . L'exemple ci-dessous encapsule un élément 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) => (
  <Button onClick={() => props.history.Push(props.to)}>
    {props.children}
  </Button>
);

NavButton.propTypes = {
  history: PropTypes.shape({
    Push: PropTypes.func.isRequired
  }),
  to: PropTypes.string.isRequired
};

export default withRouter(NavButton);

Usage:

<NavButton to="/somewhere">Click me</NavButton>
3
Rodrigo

J'ai eu du mal avec cela pendant un moment - quelque chose de si simple, mais de si compliqué, parce que ReactJS est juste une manière complètement différente d'écrire des applications web, c'est très étranger à notre peuple!

J'ai créé un composant séparé pour résorber 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 (
            <Route render={({history}) => (
                <button {...this.props}
                       onClick={() => {
                           history.Push(this.props.to)
                       }}>
                    {this.props.children}
                </button>
            )}/>
        );
    }
}

LinkButton.propTypes = {
    to: PropTypes.string.isRequired
};

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

<LinkButton className="btn btn-primary" to="/location">
    Button Text
</LinkButton>
2
mwieczorek

Je pense que @rgommezz couvre la plupart des cas moins un qui, à mon avis, est assez 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 tout composant de mon choix sans faire beaucoup de CdC sur mes composants ... 

Pourquoi personne n’a-t-il fourni cette solution auparavant? J'espère que ça aide, et si vous voyez un problème avec cela, s'il vous plaît faites le moi savoir.

2
user3053247

Comme je préfère parfois changer d’itinéraire 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 (
      <Router ref={ el => this.router = el }>
        <Link to="/signin">Sign in</Link>
        <Route path="/signin" exact={true} render={() => (
          <SignPage onFormSubmit={ this.handleSignFormSubmit } />
        )} />
      </Router>
    )
  }
}
1
piotr_cz

Pour ceux d'entre vous qui ont besoin de rediriger leur routeur avant d'initialiser complètement son routeur en utilisant React Router ou React Router Dom Vous pouvez fournir une redirection en accédant simplement à l'objet d'historique et en y insérant un nouvel état dans votre construction de app.js. Considérer ce qui suit:

function getSubdomain(hostname) {
    let regexParse = new RegExp('[a-z\-0-9]{2,63}\.[a-z\.]{2,5}$');
    let urlParts = regexParse.exec(hostname);
    return hostname.replace(urlParts[0], '').slice(0, -1);
}

class App extends Component {

    constructor(props) {
        super(props);


        this.state = {
            hostState: true
        };

        if (getSubdomain(window.location.hostname).length > 0) {
            this.state.hostState = false;
            window.history.pushState('', '', './login');
        } else {
            console.log(getSubdomain(window.location.hostname));
        }

    }


    render() {
        return (

            <BrowserRouter>
                {this.state.hostState ? (
                    <div>
                        <Route path="/login" component={LoginContainer}/>
                        <Route path="/" component={PublicContainer}/>
                    </div>
                ) : (
                    <div>
                        <Route path="/login" component={LoginContainer}/>
                    </div>
                )

                }
            </BrowserRouter>)
    }


}

Ici, nous voulons changer les itinéraires de sortie dépendant d'un sous-domaine, en interagissant avec l'objet d'historique avant que le composant ne soit restitué, nous pouvons effectivement effectuer une redirection tout en laissant nos itinéraires inact.

window.history.pushState('', '', './login');
0
li x