score:5

Accepted answer

Because you're including the header as a route, it's re-rendering every time the route changes. Just pull the header out of the route and it will stay consistent while the route-based components change:

import React from 'react'
import { Route, Switch } from 'react-router-dom'
import Home2 from './Home'
import Head from './Head'

const Main = () => (
  <main>
    <Head />
    <Switch>
      <Route exact path='/' component={Home}/>
      // Add any other routes you want here
    </Switch>
  </main>
)

export default Main;

score:-1

I had a similar problem, in the end I could not solve it, so try the following. in the update of my main route, request menu, if I do not have it created.

    #Route.js

    state = {
        menu: null
    };

    componentDidUpdate() {
            if ( !this.state.menu ) {
                this._instanceMenu();
            }
    } 

    render() {
            return (
                    <React.Fragment>
                            { this._renderStaticContent() }
                            <Switch>
                                    <Route exact path={ '/' } component={ DefaultPage } />
                                    <Route component={ Error404 } />
                            </Switch>
                    </React.Fragment>
            );
    };

The first time I request it by fetch, then I store it in localstorage, later, if it is already in localStorage, I do not return to make a fetch.

    #Route.js

    _instanceMenu() {
            if ( !this.helper.getMenu() ) {
                    this.helper.instanceMenu( ( menu ) => {
                            this.setState( { menu } );
                    } );
            }

            this.setState( { menu: this.helper.getMenu() } );
    }

Optional

    #Helper.js

    instanceMenu = ( callback ) => {
            axios.get(
                    this._urlMenu,
            ).then( ( response ) => {
                    localStorage.setItem( this._menu, JSON.stringify( response.data ) );
                    callback( this.getMenu() );
            } ).catch( ( error ) => {
                    console.log( error );
            } );
    };

    getMenu = () => {
            return JSON.parse( localStorage.getItem( this._menu ) );
    };

finally I send it to my component with a Higer-Order Component (Readme)

     #Route.js

     _renderStaticContent = () => {
            if ( !this.authService.isLoggedIn() ) return null;

            return <React.Fragment>
                    <Route component={ HeaderContainer } />
                    <Route component={ wrap( SidebarContainer, { 'menu': this.state.menu } ) } />
                    <Route component={ Footer } />
            </React.Fragment>;
    };

PD: Sorry for my english.

score:0

I also had the same issue but it wasn't related to the having the header inside a route. In fact, it was something less obvious that was difficult to pin down.

We were fetching some information from a custom hook, but this custom hook was returning the whole object from the state rather than destructuring the specific fields. Due to this, other fields inside the object (which we weren't interested in) were updating on route change so this was triggering the unnecessary re-render of the header.

Just to provide an example;

What we WERE doing (incorrect)

const { auth } = useSelector(state => state); 

What we changed it to

const { user } = useSelector(state => state.auth);

Other fields inside auth were changing but we weren't interested in those. By destructuring just the user the other fields were ignored and the re-render stopped.

I realise this is probably quite a unique scenario, but hopefully it can help somebody.

score:6

Even though it is late, I thought I'd post the answer here for other people who might be struggling with this.

First of all, as per the answer by @JulesDupont, your <Head /> component should be outside of your routes.

const App = () => (
  <>
    <Head />
    <Switch>
      <Route exact path='/' component={Component 1}/>
      // Add any other routes goes here
    </Switch>
  </>
)

export default App;

Additionally, the pattern that you are searching for is the use of the <Link> tags from react-router-dom. It would be great if you could post your Head component here. There is a strong chance that you are using <a href='/#'> tags to redirect instead of <Link to='/#'> tags inside of your <Menu />.

<a> tags would trigger an entire page reload, causing your <Head /> component to get re mounted each time you navigate to a new page, hence re-fetching all the data. However, the <Link> tag does not not trigger full page reloads.

Example:

import { Link } from 'react-router-dom';

const Head = () => {
    return (
        <ul>
            <Link to='/your-route'>Item 1 (fetches data)<Link>
            <Link to='/your-other-route'>Item 2 (fetches data)</Link>
        </ul>
    )
}

export default Head;

This will ensure that your Head component does not re-render or re-fetches data when navigating to another route.


Related Query

More Query from same tag