'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;


Solution 1:[1]

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);

Example : https://codesandbox.io/s/youthful-pare-n8p1y

Solution 2:[2]

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} />} />

Solution 3:[3]

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.

Solution 4:[4]

use useLocation and useLayoutEffect get more efficiency :

   import { useLocation } from "react-router-dom";
//...
   const location = useLocation();
//...
   useLayoutEffect(() => {
    console.log("location",location) 
    
   }, [location])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mohamed Ramrami
Solution 2 Aliens Dev
Solution 3 onmyway133
Solution 4 foad abdollahi