Respuestas:
Yo uso withRouter
para obtener el location
accesorio. 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 withRouter
y 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 history
parecen contaminar los accesorios a medida que desestructuran el objeto en él.
withRoutes
a 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.push
sí se dispara history.listen
. Consulte el ejemplo Uso de una URL base en los documentos de la historia v4 . Debido a que en history
realidad es un contenedor del history
objeto nativo de un navegador, no se comporta exactamente como el nativo.
const unlisten = history.listen(myListener); unlisten();
withRouter
, history.listen
y 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.listen
es 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 render
atributo 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 onRouteChange
método, esto podría causar el error 'Profundidad máxima de actualización excedida'.
Con el useEffect
enlace 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 useEffect
realmente 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, useLayoutEffect
ya 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);
path
es el accesorio que se pasa en el match
objeto cuando se usa withRouter
.
No estoy realmente a favor de escuchar / no escuchar manualmente en la historia. Eso es solo imo.