web-dev-qa-db-fra.com

Détecter le changement d'itinéraire avec react-router

Je dois implémenter une logique métier en fonction de l'historique de navigation.

Ce que je veux faire est quelque chose comme ceci:

reactRouter.onUrlChange(url => {
   this.history.Push(url);
});

Est-il possible de recevoir un rappel de react-router lorsque l'URL est mise à jour?

44
Aris

Vous pouvez utiliser la fonction history.listen() lorsque vous essayez de détecter le changement de route. Considérant que vous utilisez react-router v4, enveloppez votre composant avec withRouter HOC pour avoir accès à la propriété history prop.

history.listen() renvoie une fonction unlisten. Vous utiliseriez ceci pour unregister à partir de l'écoute.

Vous pouvez configurer vos itinéraires comme

index.js

ReactDOM.render(
      <BrowserRouter>
            <AppContainer>
                   <Route exact path="/" Component={...} />
                   <Route exact path="/Home" Component={...} />
           </AppContainer>
        </BrowserRouter>,
  document.getElementById('root')
);

puis dans AppContainer.js

class App extends Component {

  componentWillMount() {
    this.unlisten = this.props.history.listen((location, action) => {
      console.log("on route change");
    });
  }
  componentWillUnmount() {
      this.unlisten();
  }
  render() {
     return (
         <div>{this.props.children}</div>
      );
  }
}
export default withRouter(App);

De l'historique docs :

Vous pouvez écouter les modifications apportées à l'emplacement actuel à l'aide de history.listen:

history.listen((location, action) => {
      console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
  console.log(`The last navigation action was ${action}`)
})

L'objet location implémente un sous-ensemble de l'interface window.location, notamment:

**location.pathname** - The path of the URL
**location.search** - The URL query string
**location.hash** - The URL hash fragment

Les emplacements peuvent également avoir les propriétés suivantes:

location.state - Un état supplémentaire pour cet emplacement qui ne réside pas dans l'URL (pris en charge dans createBrowserHistory et createMemoryHistory)

location.key - Chaîne unique représentant cet emplacement (prise en charge dans createBrowserHistory et createMemoryHistory)

L'action est une de Push, REPLACE, or POP en fonction de la manière dont l'utilisateur est arrivé à l'URL actuelle.

Lorsque vous utilisez react-router v3, vous pouvez utiliser history.listen() à partir du package history comme mentionné ci-dessus ou vous pouvez également utiliser browserHistory.listen().

Vous pouvez configurer et utiliser vos itinéraires comme

import {browserHistory} from 'react-router';

class App extends React.Component {

    componentDidMount() {
          this.unlisten = browserHistory.listen( location =>  {
                console.log('route changes');

           });

    }
    componentWillUnmount() {
        this.unlisten();

    }
    render() {
        return (
               <Route path="/" onChange={yourHandler} component={AppContainer}>
                   <IndexRoute component={StaticContainer}  />
                   <Route path="/a" component={ContainerA}  />
                   <Route path="/b" component={ContainerB}  />
            </Route>
        )
    }
} 
67
Shubham Khatri

Si vous souhaitez écouter l'objet history globalement, vous devez le créer vous-même et le transmettre à l'objet Router. Ensuite, vous pouvez l’écouter avec sa méthode listen():

// Use Router from react-router, not BrowserRouter.
import { Router } from 'react-router';

// Create history object.
import createHistory from 'history/createBrowserHistory';
const history = createHistory();

// Listen to history changes.
// You can unlisten by calling the constant (`unlisten()`).
const unlisten = history.listen((location, action) => {
  console.log(action, location.pathname, location.state);
});

// Pass history to Router.
<Router history={history}>
   ...
</Router>

Mieux encore, si vous créez l’objet historique en tant que module, vous pourrez ainsi l’importer facilement où vous en aurez besoin (par exemple, import history from './history';

10
Fabian Schultz

Je suis tombé sur cette question alors que j'essayais de centrer le lecteur d'écran ChromeVox vers le haut de "l'écran" après avoir accédé à un nouvel écran dans une application React page. Essentiellement, essayer d'imiter ce qui se produirait si cette page était chargée en suivant un lien vers une nouvelle page Web rendue par le serveur.

Cette solution ne nécessite aucun écouteur, elle utilise withRouter() et la méthode componentDidUpdate() cycle de vie pour déclencher un clic permettant de focaliser ChromeVox sur l'élément souhaité lors de la navigation vers un nouveau chemin d'URL.


La mise en oeuvre

J'ai créé un composant "Screen" qui est enroulé autour de l'étiquette de commutateur react-router qui contient tous les écrans d'applications.

<Screen>
  <Switch>
    ... add <Route> for each screen here...
  </Switch>
</Screen>

Screen.tsx Composant

Remarque: Ce composant utilise React + TypeScript

import React from 'react'
import { RouteComponentProps, withRouter } from 'react-router'

class Screen extends React.Component<RouteComponentProps> {
  public screen = React.createRef<HTMLDivElement>()
  public componentDidUpdate = (prevProps: RouteComponentProps) => {
    if (this.props.location.pathname !== prevProps.location.pathname) {
      // Hack: setTimeout delays click until end of current
      // event loop to ensure new screen has mounted.
      window.setTimeout(() => {
        this.screen.current!.click()
      }, 0)
    }
  }
  public render() {
    return <div ref={this.screen}>{this.props.children}</div>
  }
}

export default withRouter(Screen)

J'avais essayé d'utiliser focus() à la place de click(), mais en cliquant sur ce bouton, ChromeVox arrête de lire tout ce qu'il lit actuellement et recommence où je le dis.

Remarque avancée: Dans cette solution, la navigation <nav> qui, à l'intérieur du composant Screen et restituée après le contenu de <main>, est visuellement positionnée au-dessus de la main à l'aide de css order: -1;. Donc, en pseudo-code:

<Screen style={{ display: 'flex' }}>
  <main>
  <nav style={{ order: -1 }}>
<Screen>

Si vous avez des idées, des commentaires ou des conseils sur cette solution, veuillez ajouter un commentaire.

1
Beau Smith