Risposte:
Uso withRouterper ottenere l' locationelica. Quando il componente viene aggiornato a causa di una nuova route, controllo se il valore è cambiato:
@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>
}
}
Spero che sia d'aiuto
withRouter, ma ricevo un errore You should not use <Route> or withRouter() outside a <Router>. Non vedo alcun <Router/>componente nel codice sopra. Quindi come funziona?
<Switch>componente agisce come router di fatto. <Route>Verrà eseguito il rendering solo della prima voce con un percorso corrispondente. Non è necessario alcun <Router/>componente in questo scenario
Per espandere quanto sopra, dovrai accedere all'oggetto della cronologia. Se stai usando BrowserRouter, puoi importare withRoutere avvolgere il tuo componente con un componente di ordine superiore (HoC) per avere accesso tramite oggetti di scena alle proprietà e alle funzioni dell'oggetto storico.
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);
L'unica cosa di cui essere consapevoli è che con Router e la maggior parte degli altri modi per accedervi historysembrano inquinare gli oggetti di scena mentre destrutturano l'oggetto al suo interno.
withRoutesa withRouter.
Dovresti usare la cronologia v4 v.
Esempio da lì
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.pushsi inneschi history.listen. Vedi l'esempio sull'utilizzo di un URL di base nella cronologia dei documenti v4 . Poiché in historyrealtà è un wrapper historydell'oggetto nativo di un browser, non si comporta esattamente come quello nativo.
const unlisten = history.listen(myListener); unlisten();
withRouter, history.listenE useEffect(Reagire Ganci) funziona abbastanza bene insieme:
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)
Il callback dell'ascoltatore si attiverà ogni volta che si cambia una rotta e il ritorno per history.listenè un gestore di spegnimento che funziona bene useEffect.
v5.1 introduce l'hook utile 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>
}
Con ganci:
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)
Importare e renderizzare come <DebugHistory>componente
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]);
}
con ganci
Con reagire Hooks, sto usando 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 alcuni casi è possibile utilizzare l' renderattributo anziché component, in questo modo:
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>
}
}
Si noti che se si cambia stato nel onRouteChangemetodo, ciò potrebbe causare l'errore "Profondità di aggiornamento massima superata".
Con l' useEffecthook è possibile rilevare le modifiche del percorso senza aggiungere un ascoltatore.
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);
Ho appena affrontato questo problema, quindi aggiungerò la mia soluzione come supplemento alle altre risposte fornite.
Il problema qui è che useEffectnon funziona davvero come si vorrebbe, poiché la chiamata viene attivata solo dopo il primo rendering, quindi c'è un ritardo indesiderato.
Se usi un manager statale come Redux, è probabile che otterrai uno sfarfallio sullo schermo a causa dello stato persistente nel negozio.
Quello che vuoi davvero è usare useLayoutEffectpoiché questo viene attivato immediatamente.
Quindi ho scritto una piccola funzione di utilità che ho inserito nella stessa directory del mio router:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
Che chiamo dall'interno del componente HOC in questo modo:
callApis(() => getTopicById({topicId}), path);
pathè l'elica che viene passata matchnell'oggetto durante l'utilizzo withRouter.
Non sono davvero favorevole all'ascolto / non ascolto manuale della storia. Questo è solo imo.