React useEffect is not triggering on route change
I expect that console.log('Refresh')
runs every time the route changes (switching from Component1 to Component2). But it’s only triggering on first render. Why?
index.js
:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'));
App.js
:
import React, { useEffect } from 'react';
import { Switch, Route } from 'react-router-dom';
import Nav from './Nav';
import Component1 from './Component1';
import Component2 from './Component2';
const App = () => {
useEffect( () => console.log('Refresh'));
return (
[<Switch>
<Route component = {Nav}/>
</Switch>,
<Switch>
<Route exact path="/component1" component = {Component1}/>
<Route exact path="/component2" component = {Component2}/>
</Switch>]
);
}
export default App;
Nav.js
:
import React from 'react';
import { Link } from 'react-router-dom';
const Nav = () => {
return (
<div>
<Link to = '/component1'>Component 1</Link>
<Link to = '/component2'>Component 2</Link>
</div>
);
}
export default Nav;
Component1.js
:
import React from 'react';
const Component1 = () => {
return (
<div>
<p>Hi</p>
</div>
);
}
export default Component1;
Component2.js
:
import React from 'react';
const Component2 = () => {
return (
<div>
<p>Bye</p>
</div>
);
}
export default Component2;
The useEffect
is not triggered because the App
component is not re-rendered, nothing changed in that component (no state or props update).
If you want the App
component to re-render when the route change, you can use the withRouter
HOC to inject route props, like this :
import { Switch, Route, withRouter } from 'react-router-dom';
const App = () => {
useEffect( () => console.log('Refresh'));
return (...);
}
export default withRouter(App);
use the key attribute so everytime we render new component (different key)
<Route path="/mypath/:username" exact render= {routeProps =><MyCompo {...routeProps} key={document.location.href} />} />
Use the 2nd argument to useEffect to conditionally apply effect. For example via react-router-dom, you get some properties
const { schoolId, classId } = props
useEffect(() => {
// fetch something here
}, [schoolId, classId)
Here [schoolId, classId
acts as the unique identifier for useEffect to trigger.
use useLocation
and useLayoutEffect
get more efficiency :
import { useLocation } from "react-router-dom";
//...
const location = useLocation();
//...
useLayoutEffect(() => {
console.log("location",location)
}, [location])