Ich habe ein paar Schaltflächen, die als Routen dienen. Jedes Mal, wenn die Route geändert wird, möchte ich sicherstellen, dass sich die aktive Schaltfläche ändert.
Gibt es eine Möglichkeit, Routenänderungen in React Router v4 abzuhören?
Ich habe ein paar Schaltflächen, die als Routen dienen. Jedes Mal, wenn die Route geändert wird, möchte ich sicherstellen, dass sich die aktive Schaltfläche ändert.
Gibt es eine Möglichkeit, Routenänderungen in React Router v4 abzuhören?
Antworten:
Ich benutze withRouter
, um die location
Requisite zu bekommen . Wenn die Komponente aufgrund einer neuen Route aktualisiert wird, überprüfe ich, ob sich der Wert geändert hat:
@withRouter
class App extends React.Component {
static propTypes = {
location: React.PropTypes.object.isRequired
}
// ...
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.onRouteChanged();
}
}
onRouteChanged() {
console.log("ROUTE CHANGED");
}
// ...
render(){
return <Switch>
<Route path="/" exact component={HomePage} />
<Route path="/checkout" component={CheckoutPage} />
<Route path="/success" component={SuccessPage} />
// ...
<Route component={NotFound} />
</Switch>
}
}
Ich hoffe es hilft
withRouter
, aber ich bekomme eine Fehlermeldung You should not use <Route> or withRouter() outside a <Router>
. Ich sehe keine <Router/>
Komponente im obigen Code. Wie funktioniert es?
<Switch>
Komponente als De-facto-Router. Es wird nur der erste <Route>
Eintrag gerendert, der einen passenden Pfad hat. <Router/>
In diesem Szenario ist keine Komponente
Um das oben Gesagte zu erweitern, müssen Sie zum Verlaufsobjekt gelangen. Wenn Sie verwenden BrowserRouter
, können Sie withRouter
Ihre Komponente importieren und mit einer Komponente höherer Ordnung (HoC) umschließen , um über Requisiten auf die Eigenschaften und Funktionen des Verlaufsobjekts zugreifen zu können.
import { withRouter } from 'react-router-dom';
const myComponent = ({ history }) => {
history.listen((location, action) => {
// location is an object like window.location
console.log(action, location.pathname, location.state)
});
return <div>...</div>;
};
export default withRouter(myComponent);
Das einzige, was Sie beachten müssen, ist, dass withRouter und die meisten anderen Zugriffsmöglichkeiten history
auf die Requisiten verschmutzen, wenn sie das Objekt darin destrukturieren .
withRoutes
zu withRouter
.
Sie sollten history v4 lib verwenden.
Beispiel von dort
history.listen((location, action) => {
console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
console.log(`The last navigation action was ${action}`)
})
history.push
das auslöst history.listen
. Weitere Informationen finden Sie im Beispiel Verwenden einer Basis-URL in Verlauf v4-Dokumenten . Da dies history
tatsächlich ein Wrapper des nativen history
Objekts eines Browsers ist, verhält es sich nicht genau wie das native.
const unlisten = history.listen(myListener); unlisten();
withRouter
, history.listen
Und useEffect
(React Hooks) funktioniert zwar recht gut zusammen:
import React, { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
const Component = ({ history }) => {
useEffect(() => history.listen(() => {
// do something on route change
// for my example, close a drawer
}), [])
//...
}
export default withRouter(Component)
Der Listener-Rückruf wird jedes Mal ausgelöst, wenn eine Route geändert wird, und die Rückgabe für history.listen
ist ein Shutdown-Handler, mit dem gut gespielt werden kann useEffect
.
v5.1 führt den nützlichen Hook ein useLocation
https://reacttraining.com/blog/react-router-v5-1/#uselocation
import { Switch, useLocation } from 'react-router-dom'
function usePageViews() {
let location = useLocation()
useEffect(
() => {
ga.send(['pageview', location.pathname])
},
[location]
)
}
function App() {
usePageViews()
return <Switch>{/* your routes here */}</Switch>
}
Cannot read property 'location' of undefined at useLocation
. Sie müssen sicherstellen, dass sich der Aufruf von useLocation () nicht in derselben Komponente befindet, die den Router in den Baum
Mit Haken:
import { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { history as historyShape } from 'react-router-prop-types'
const DebugHistory = ({ history }) => {
useEffect(() => {
console.log('> Router', history.action, history.location])
}, [history.location.key])
return null
}
DebugHistory.propTypes = { history: historyShape }
export default withRouter(DebugHistory)
Als <DebugHistory>
Komponente importieren und rendern
import React, { useEffect } from 'react';
import { useLocation } from 'react-router';
function MyApp() {
const location = useLocation();
useEffect(() => {
console.log('route has been changed');
...your code
},[location.pathname]);
}
mit Haken
Mit React Hooks verwende ich useEffect
const history = useHistory()
const queryString = require('query-string')
const parsed = queryString.parse(location.search)
const [search, setSearch] = useState(parsed.search ? parsed.search : '')
useEffect(() => {
const parsedSearch = parsed.search ? parsed.search : ''
if (parsedSearch !== search) {
// do some action! The route Changed!
}
}, [location.search])
In einigen Fällen können Sie auf diese Weise ein render
Attribut anstelle von verwenden component
:
class App extends React.Component {
constructor (props) {
super(props);
}
onRouteChange (pageId) {
console.log(pageId);
}
render () {
return <Switch>
<Route path="/" exact render={(props) => {
this.onRouteChange('home');
return <HomePage {...props} />;
}} />
<Route path="/checkout" exact render={(props) => {
this.onRouteChange('checkout');
return <CheckoutPage {...props} />;
}} />
</Switch>
}
}
Beachten Sie, dass beim Ändern des Status in der onRouteChange
Methode der Fehler "Maximale Aktualisierungstiefe überschritten" auftreten kann.
Mit dem useEffect
Hook können Routenänderungen erkannt werden, ohne dass ein Listener hinzugefügt werden muss.
import React, { useEffect } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import Main from './Main';
import Blog from './Blog';
const App = ({history}) => {
useEffect( () => {
// When route changes, history.location.pathname changes as well
// And the code will execute after this line
}, [history.location.pathname]);
return (<Switch>
<Route exact path = '/' component = {Main}/>
<Route exact path = '/blog' component = {Blog}/>
</Switch>);
}
export default withRouter(App);
Ich habe mich gerade mit diesem Problem befasst, daher werde ich meine Lösung als Ergänzung zu anderen Antworten hinzufügen.
Das Problem hierbei ist, dass useEffect
es nicht wirklich so funktioniert, wie Sie es möchten, da der Aufruf erst nach dem ersten Rendern ausgelöst wird und es zu einer unerwünschten Verzögerung kommt.
Wenn Sie einen Statusmanager wie Redux verwenden, besteht die Möglichkeit, dass Sie aufgrund des anhaltenden Status im Geschäft ein Flimmern auf dem Bildschirm erhalten.
Was Sie wirklich wollen, ist zu verwenden, useLayoutEffect
da dies sofort ausgelöst wird.
Also habe ich ein kleines Dienstprogramm geschrieben, das ich im selben Verzeichnis wie mein Router abgelegt habe:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
Was ich innerhalb der Komponente HOC so aufrufe:
callApis(() => getTopicById({topicId}), path);
path
ist die Requisite, die bei der match
Verwendung im Objekt übergeben wird withRouter
.
Ich bin nicht wirklich dafür, die Geschichte manuell anzuhören / abzuhören. Das ist nur imo.