Respuestas:
Yo uso withRouterpara obtener el locationaccesorio. Cuando el componente se actualiza debido a una nueva ruta, verifico si el valor cambió:
@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>
}
}
Espero eso ayude
withRouter, pero recibo un error You should not use <Route> or withRouter() outside a <Router>. No veo ningún <Router/>componente en el código anterior. Entonces, ¿cómo está funcionando?
<Switch>componente está actuando como el enrutador de facto. Solo se representará la primera <Route>entrada que tenga una ruta coincidente. No hay necesidad de ningún <Router/>componente en este escenario
Para ampliar lo anterior, deberá acceder al objeto de historial. Si está utilizando BrowserRouter, puede importar withRoutery envolver su componente con un componente de orden superior (HoC) para tener acceso a través de accesorios a las propiedades y funciones del objeto de historial.
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);
Lo único que debe tener en cuenta es que con Router y la mayoría de las otras formas de acceder a él historyparecen contaminar los accesorios a medida que desestructuran el objeto en él.
withRoutesa withRouter.
Debes usar history v4 lib.
Ejemplo a partir de ahí
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.pushsí se dispara history.listen. Consulte el ejemplo Uso de una URL base en los documentos de la historia v4 . Debido a que en historyrealidad es un contenedor del historyobjeto nativo de un navegador, no se comporta exactamente como el nativo.
const unlisten = history.listen(myListener); unlisten();
withRouter, history.listeny useEffect(React Hooks) funcionan muy bien juntos:
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)
La devolución de llamada del oyente se activará cada vez que se cambie una ruta, y el retorno history.listenes un controlador de apagado que funciona bien con useEffect.
v5.1 presenta el útil gancho 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. Debe asegurarse de que la llamada useLocation () no esté en el mismo componente que coloca el enrutador en el árbol: consulte aquí
Con ganchos:
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)
Importar y renderizar como <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 ganchos
Con reaccionar ganchos, estoy 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])
En algunos casos, puede usar el renderatributo en lugar de component, de esta manera:
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>
}
}
Tenga en cuenta que si cambia el estado del onRouteChangemétodo, esto podría causar el error 'Profundidad máxima de actualización excedida'.
Con el useEffectenlace es posible detectar cambios de ruta sin agregar un oyente.
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);
Acabo de resolver este problema, así que agregaré mi solución como suplemento a otras respuestas dadas.
El problema aquí es que useEffectrealmente no funciona como desearía, ya que la llamada solo se activa después del primer renderizado, por lo que hay un retraso no deseado.
Si utiliza algún administrador de estado como redux, es probable que obtenga un parpadeo en la pantalla debido al estado persistente en la tienda.
Lo que realmente quieres es usar, useLayoutEffectya que esto se activa de inmediato.
Entonces escribí una pequeña función de utilidad que puse en el mismo directorio que mi enrutador:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
Lo que llamo desde dentro del componente HOC como este:
callApis(() => getTopicById({topicId}), path);
pathes el accesorio que se pasa en el matchobjeto cuando se usa withRouter.
No estoy realmente a favor de escuchar / no escuchar manualmente en la historia. Eso es solo imo.