Ich muss je nach Browserverlauf eine Geschäftslogik implementieren.
Was ich tun möchte, ist ungefähr so:
reactRouter.onUrlChange(url => {
this.history.push(url);
});
Gibt es eine Möglichkeit, einen Rückruf vom React-Router zu erhalten, wenn die URL aktualisiert wird?
Antworten:
Sie können die
history.listen()
Funktion verwenden, wenn Sie versuchen, die Routenänderung zu erkennen. Wenn Sie verwendenreact-router v4
, wickeln Sie Ihre Komponente mitwithRouter
HOC ein, um Zugriff auf diehistory
Requisite zu erhalten.history.listen()
gibt eineunlisten
Funktion zurück. Sie würden dies verwenden, umunregister
zuzuhören.Sie können Ihre Routen wie konfigurieren
index.js
ReactDOM.render( <BrowserRouter> <AppContainer> <Route exact path="/" Component={...} /> <Route exact path="/Home" Component={...} /> </AppContainer> </BrowserRouter>, document.getElementById('root') );
und dann in 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);
Aus der Geschichte docs :
Wenn Sie verwenden , reagieren-Router v3 können Sie Gebrauch machen
history.listen()
vonhistory
Paket wie oben erwähnt , oder Sie können auch Gebrauch machenbrowserHistory.listen()
Sie können Ihre Routen wie konfigurieren und verwenden
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> ) } }
quelle
react-router v4
"withRouter
history.listen()
bei der Verwendung vonwithRouter
bereits aktualisiert Ihre Komponente mit neuen Requisiten tritt jedes Mal Routing? Sie können einen einfachen Vergleich vonnextProps.location.href === this.props.location.href
in durchführencomponentWillUpdate
, um alles auszuführen, was Sie tun müssen, wenn es sich geändert hat.Update für 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> ); };
quelle
Wenn Sie das
history
Objekt global anhören möchten , müssen Sie es selbst erstellen und an das übergebenRouter
. Dann können Sie es mit seinerlisten()
Methode anhören :// 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>
Noch besser, wenn Sie das Verlaufsobjekt als Modul erstellen, damit Sie es problemlos überall dort importieren können, wo Sie es benötigen (z
import history from './history';
quelle
react-router v6
In der kommenden Version 6 kann dies durch Kombinieren von
useLocation
unduseEffect
Hooks erfolgenimport { 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]) ... }
Zur bequemen Wiederverwendung können Sie dies in einem benutzerdefinierten
useLocationChange
Haken tun// 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) }) ... }
Wenn Sie bei Änderungen auch die vorherige Route sehen müssen, können Sie diese mit einem
usePrevious
Haken kombinierenconst 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) }) ... }
Es ist wichtig zu beachten, dass alle oben genannten Feuer auf der ersten gemounteten Client-Route sowie nachfolgende Änderungen auftreten. Wenn dies ein Problem ist, verwenden Sie das letztere Beispiel und überprüfen Sie, ob ein
prevLocation
vorhanden ist, bevor Sie etwas unternehmen.quelle
location
hier ist der Browser-Speicherort, also ist er in jeder Komponente gleich und in diesem Sinne immer korrekt. Wenn Sie den Hook in verschiedenen Komponenten verwenden, erhalten alle die gleichen Werte, wenn sich der Standort ändert. Ich denke, was sie mit diesen Informationen machen, wird anders sein, aber es ist immer konsistent.Dies ist eine alte Frage, und ich verstehe die geschäftliche Notwendigkeit, auf Routenänderungen zu achten, um eine Routenänderung voranzutreiben, nicht ganz. scheint Kreisverkehr.
ABER wenn Sie hier gelandet sind, weil Sie lediglich die Routenänderung
'page_path'
auf einem React-Router für Google Analytics / globales Site-Tag / ähnliches aktualisieren wollten, können Sie jetzt einen Hook verwenden. Ich habe es basierend auf der akzeptierten Antwort geschrieben: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]) }
Sie sollten diesen Hook einmal in Ihrer App verwenden, irgendwo in der Nähe der Oberseite, aber immer noch in einem Router. Ich habe es auf einem
App.js
, der so aussieht: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> )
quelle
Ich bin auf diese Frage gestoßen, als ich versuchte, den ChromeVox-Bildschirmleser auf den oberen Rand des "Bildschirms" zu fokussieren, nachdem ich in einer React-App für einzelne Seiten zu einem neuen Bildschirm navigiert hatte. Grundsätzlich wird versucht zu emulieren, was passieren würde, wenn diese Seite geladen würde, indem Sie einem Link zu einer neuen vom Server gerenderten Webseite folgen.
Für diese Lösung sind keine Listener erforderlich. Sie verwendet
withRouter()
diecomponentDidUpdate()
Lifecycle-Methode, um einen Klick auszulösen und ChromeVox auf das gewünschte Element zu fokussieren, wenn Sie zu einem neuen URL-Pfad navigieren.Implementierung
Ich habe eine "Bildschirm" -Komponente erstellt, die um das React-Router-Switch-Tag gewickelt ist, das alle Apps-Bildschirme enthält.
<Screen> <Switch> ... add <Route> for each screen here... </Switch> </Screen>
Screen.tsx
KomponenteHinweis: Diese Komponente verwendet 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)
Ich hatte versucht,
focus()
anstelle von zu verwendenclick()
, aber durch Klicken hört ChromeVox auf zu lesen, was gerade gelesen wird, und startet erneut dort, wo ich es anweisen soll.Erweiterter Hinweis: In dieser Lösung wird die Navigation,
<nav>
die innerhalb der Bildschirmkomponente angezeigt und nach dem<main>
Inhalt gerendert wird, visuell über demmain
verwendeten CSS positioniertorder: -1;
. Also im Pseudocode:<Screen style={{ display: 'flex' }}> <main> <nav style={{ order: -1 }}> <Screen>
Wenn Sie Gedanken, Kommentare oder Tipps zu dieser Lösung haben, fügen Sie bitte einen Kommentar hinzu.
quelle
Reagiere auf Router V5
Wenn Sie den Pfadnamen als Zeichenfolge ('/' oder 'Benutzer') möchten, können Sie Folgendes verwenden:
// React Hooks: React Router DOM let history = useHistory(); const location = useLocation(); const pathName = location.pathname;
quelle
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]) }
quelle