Ali Mert Çakar
Ali Mert Çakar

Reputation: 326

How to make components consist between pages?

When you go to about page from index page, RestOfTheApp rerenders. Maybe it should be this way with SSR, but nextjs added static rendering like gatsby. Isn't there a way to prevent components rerendering? Like: a header shouldn't change between pages.

about.js

function index() {
  return (
    <>
        <RestOfTheApp> 
          about
        </RestOfTheApp>
    </>
  )
}

index.js

 function index() {
      return (
        <>
        <RestOfTheApp> 
          index
        </RestOfTheApp>
        </>
      )
    }

You can keep component state with redux I assume, but whole page re-rendering when you just need to fetch some blog content seems bloaty. I've tested with some basic layout, it still seems lightning fast but not re-rendering whole page is the main concept of SPA's, I am a little heart broken 💔

Upvotes: 1

Views: 5767

Answers (2)

Chen Ni
Chen Ni

Reputation: 1144

@Ankit has already given a great answer on HOW to create a persistent component. Here is WHY it works.

So what really happens when you navigate from some page A (defined in pages/a.js) to another page B (defined in pages/b.js)? First, the navigation takes place on the client-side. This means instead of fetching the rendered HTML from the server, some JavaScript is run in the browser to render the new page. (You can verify it here.) The JavaScript logic of page navigation boils down to this:

  1. The JavaScript code of the new page component <B /> is fetched from the server, if it's not already prefetched;
  2. Next.js will call ReactDOM.render() with 2 arguments: the first one is the new React element to render (it can be roughly thought of as the updated App component), and the second one is the DOM container element (it's always <div id="__next"></div>) that the new React element is rendered into.

In short, this process can be roughly thought of as rendering the updated App component into the <div id="__next"></div> DOM container element. React will then take care of diffing the new and old React elements and decide which part of the DOM to re-render and which part to update.

So what does the new and old React element look like? Well, the default definition of the App component looks like this:

import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  // Component will be set to the current page component
  return <Component {...pageProps} />
}

export default MyApp

Where the Component variable will be set to the current page component. This means the old React element will look like this:

<A {...pageProps} />

and the new React element will look like this:

<B {...pageProps} />

According to the React diffing algorithm, when comparing the new and old React element, if the two elements being compared are of different types, then the corresponding subtree will be entirely destroyed and re-rendered.

That's exactly what happens in this case. <A /> and <B /> are two different components and are considered as of different types, so the part of the DOM that corresponds to <A /> will be destroyed and re-rendered as <B />.

That's why the entire page component will be re-rendered when you navigate to a new page, even if they include common components like the header.

If you put the header in the custom App components, like this:

import Header from '../components/header'

function MyApp({ Component, pageProps }) {
  return (
    <div>
      <Header />
      <Component {...pageProps} />
    </div>
  )
}

export default MyApp

Then the <Header /> component will persist across page navigations, because the React diffing algorithm will consider it as the same type and will only make minimal updates to the subtree.

Upvotes: 5

Ankit Gupta
Ankit Gupta

Reputation: 787

Every component that you include in a page (under /pages) will re-render no matter what you do. But it's definitely possible to add a persistent layout which doesn't re-render in NextJs. The solution is the custom app component. You can read more about it here

Following example can be helpful to understand how you can create a persistent layout:

// /pages/_app.js

import React from 'react';
import App from 'next/app';
import Layout from '../components/Layout';

class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props

    return (
      <Layout>
        <Component {...pageProps}></Component>
      </Layout>
    )
  }
}

export default MyApp 

In this case, the Layout component will not re-render when you navigate between pages.

Upvotes: 4

Related Questions