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);
});
Existe-t-il un moyen de recevoir un rappel de react-router lorsque l'URL est mise à jour?
Réponses:
Vous pouvez utiliser la
history.listen()
fonction lorsque vous essayez de détecter le changement d'itinéraire. Considérant que vous utilisezreact-router v4
, enveloppez votre composant avecwithRouter
HOC pour accéder à l'history
accessoire.history.listen()
renvoie uneunlisten
fonction. Vous utiliseriez cela pourunregister
écouter.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);
À partir des documents d' histoire :
Lorsque vous utilisez réagir-routeur v3 , vous pouvez utiliser
history.listen()
duhistory
paquet comme mentionné ci - dessus ou vous pouvez également utiliserbrowserHistory.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> ) } }
la source
react-router v4
"withRouter
history.listen()
lorsque vous utilisezwithRouter
déjà des mises à jour de votre composant avec de nouveaux accessoires à chaque fois que le routage se produit? Vous pouvez faire une simple comparaison denextProps.location.href === this.props.location.href
incomponentWillUpdate
pour effectuer tout ce que vous devez faire s'il a changé.Mise à jour pour React Router 5.1.
import React from 'react'; import { useLocation, Switch } from 'react-router-dom'; const App = () => { const location = useLocation(); React.useEffect(() => { console.log('Location changed'); }, [location]); return ( <Switch> {/* Routes go here */} </Switch> ); };
la source
Si vous souhaitez écouter l'
history
objet globalement, vous devrez le créer vous-même et le transmettre auRouter
. Ensuite, vous pouvez l'écouter avec salisten()
méthode:// 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>
Encore mieux si vous créez l'objet historique en tant que module, vous pouvez donc facilement l'importer partout où vous en avez besoin (par exemple
import history from './history';
la source
react-router v6
Dans la prochaine v6 , cela peut être fait en combinant les crochets
useLocation
etuseEffect
import { useLocation } from 'react-router-dom'; const MyComponent = () => { const location = useLocation() React.useEffect(() => { // runs on location, i.e. route, change console.log('handle route change here', location) }, [location]) ... }
Pour une réutilisation pratique, vous pouvez le faire dans un
useLocationChange
crochet personnalisé// runs action(location) on location, i.e. route, change const useLocationChange = (action) => { const location = useLocation() React.useEffect(() => { action(location) }, [location]) } const MyComponent1 = () => { useLocationChange((location) => { console.log('handle route change here', location) }) ... } const MyComponent2 = () => { useLocationChange((location) => { console.log('and also here', location) }) ... }
Si vous avez également besoin de voir l'itinéraire précédent lors du changement, vous pouvez le combiner avec un
usePrevious
crochetconst usePrevious(value) { const ref = React.useRef() React.useEffect(() => { ref.current = value }) return ref.current } const useLocationChange = (action) => { const location = useLocation() const prevLocation = usePrevious(location) React.useEffect(() => { action(location, prevLocation) }, [location]) } const MyComponent1 = () => { useLocationChange((location, prevLocation) => { console.log('changed from', prevLocation, 'to', location) }) ... }
Il est important de noter que tout ce qui précède se déclenche sur la première route client en cours de montage, ainsi que sur les modifications ultérieures. Si c'est un problème, utilisez le dernier exemple et vérifiez que a
prevLocation
existe avant de faire quoi que ce soit.la source
location
voici l'emplacement du navigateur, donc c'est le même dans chaque composant et toujours correct dans ce sens. Si vous utilisez le hook dans différents composants, ils recevront tous les mêmes valeurs lorsque l'emplacement change. Je suppose que ce qu'ils font avec ces informations sera différent, mais c'est toujours cohérent.C'est une vieille question et je ne comprends pas tout à fait le besoin commercial d'écouter les changements d'itinéraire pour pousser un changement d'itinéraire; semble rond-point.
MAIS si vous vous êtes retrouvé ici parce que tout ce que vous vouliez était de mettre à jour le
'page_path'
changement de route sur un routeur de réaction pour google analytics / global site tag / quelque chose de similaire, voici un hook que vous pouvez maintenant utiliser. Je l'ai écrit sur la base de la réponse acceptée:useTracking.js
import { useEffect } from 'react' import { useHistory } from 'react-router-dom' export const useTracking = (trackingId) => { const { listen } = useHistory() useEffect(() => { const unlisten = listen((location) => { // if you pasted the google snippet on your index.html // you've declared this function in the global if (!window.gtag) return window.gtag('config', trackingId, { page_path: location.pathname }) }) // remember, hooks that add listeners // should have cleanup to remove them return unlisten }, [trackingId, listen]) }
Vous devez utiliser ce hook une fois dans votre application, quelque part près du sommet mais toujours à l'intérieur d'un routeur. Je l'ai sur un
App.js
qui ressemble à ceci:App.js
import * as React from 'react' import { BrowserRouter, Route, Switch } from 'react-router-dom' import Home from './Home/Home' import About from './About/About' // this is the file above import { useTracking } from './useTracking' export const App = () => { useTracking('UA-USE-YOURS-HERE') return ( <Switch> <Route path="/about"> <About /> </Route> <Route path="/"> <Home /> </Route> </Switch> ) } // I find it handy to have a named export of the App // and then the default export which wraps it with // all the providers I need. // Mostly for testing purposes, but in this case, // it allows us to use the hook above, // since you may only use it when inside a Router export default () => ( <BrowserRouter> <App /> </BrowserRouter> )
la source
Je suis tombé sur cette question alors que j'essayais de concentrer le lecteur d'écran ChromeVox en haut de «l'écran» après avoir navigué vers un nouvel écran dans une application React à une seule page. Essentiellement, essayer d'émuler ce qui se passerait 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 lacomponentDidUpdate()
méthode du cycle de vie pour déclencher un clic pour concentrer 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 la balise de commutation react-router qui contient tous les écrans des applications.
<Screen> <Switch> ... add <Route> for each screen here... </Switch> </Screen>
Screen.tsx
ComposantRemarque: 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 à la
focus()
place declick()
, mais le clic fait que ChromeVox arrête de lire ce qu'il est en train de lire et recommence là où je lui dis de commencer.Note avancée: Dans cette solution, la navigation
<nav>
qui à l'intérieur du composant Screen et rendue après le<main>
contenu est visuellement positionnée au-dessus de l'main
utilisation de cssorder: -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.
la source
React Router V5
Si vous voulez que le pathName soit une chaîne ('/' ou 'users'), vous pouvez utiliser ce qui suit:
// React Hooks: React Router DOM let history = useHistory(); const location = useLocation(); const pathName = location.pathname;
la source
import React from 'react'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import Sidebar from './Sidebar'; import Chat from './Chat'; <Router> <Sidebar /> <Switch> <Route path="/rooms/:roomId" component={Chat}> </Route> </Switch> </Router>
import { useHistory } from 'react-router-dom'; function SidebarChat(props) { **const history = useHistory();** var openChat = function (id) { **//To navigate** history.push("/rooms/" + id); } }
**//To Detect the navigation change or param change** import { useParams } from 'react-router-dom'; function Chat(props) { var { roomId } = useParams(); var roomId = props.match.params.roomId; useEffect(() => { //Detect the paramter change }, [roomId]) useEffect(() => { //Detect the location/url change }, [location]) }
la source