Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NEXT JS - How to prevent layout get re-mounted?

Trying next with layout pattern:

https://github.com/zeit/next.js/tree/canary/examples/layout-component

And the problem is that Layout component get remounted on every page change. I need to use layout component as a Container so it'll fetch data from server on every mount. How can I prevent layout to get re-mounted? Or am I missing something there?

like image 615
Anton Avatar asked Dec 29 '19 12:12

Anton


People also ask

Why does my nextjs page keep re-rendering?

You have a pretty good understanding of what's happening. All pages in Next.js depend on _app - it rerenders because of pageProps or more likely Component prop changes - forcing the children to rerender.

Why do we need nested layouts in ReactJS?

Since we're returning a function, we can have complex nested layouts if desired. When navigating between pages, we want to persist page state (input values, scroll position, etc.) for a Single-Page Application (SPA) experience. This layout pattern enables state persistence because the React component tree is maintained between page transitions.

Why is layout being mounted again and again?

Even though this is the topic Layoutbeing mounted again and again, the root cause of this problem is that you have some data loaded in some child component which is getting fetched again and again.

What is layout layout component in react?

layout-component The React model allows us to deconstruct a page into a series of components. Many of these components are often reused between pages. For example, you might have the same navigation bar and footer on every page.


3 Answers

If you put your Layout component inside page component it will be re-remounted on page navigation (page switch).

You can wrap your page component with your Layout component inside _app.js, it should prevent it from re-mounting.

Something like this:

// _app.js
import Layout from '../components/Layout';

class MyApp extends App {
 static async getInitialProps(appContext) {
    const appProps = await App.getInitialProps(appContext);
    return {
      ...appProps,
    };
  }

  render() {
    const { Component, pageProps } = this.props;

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

export default MyApp;
like image 198
felixmosh Avatar answered Oct 21 '22 06:10

felixmosh


This helped me for persistent layouts. The author puts together a function that wraps your page components in your Layout component and then passes that fetch function to your _app.js. This way the _app.js is actually the components that renders the Layout but you get to specify which pages use which layout (in case you have multiple layouts).

So you have the flexibility of having multiple layouts throughout your site but those pages that share the same layout will actually share the same layout component and it will not have to be remounted on navigation.

Here is the link to the full article Persistent Layout Patterns in Next.js

Here are the important code snippets. A page and then _app.js

// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'

const AccountSettingsBasicInformation = () => (
  <div>{/* ... */}</div>
)

AccountSettingsBasicInformation.getLayout = page => (
  <SiteLayout>
    <AccountSettingsLayout>{page}</AccountSettingsLayout>
  </SiteLayout>
)

export default AccountSettingsBasicInformation
// /pages/_app.js
import React from 'react'
import App from 'next/app'

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

    const getLayout = Component.getLayout || (page => page)

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

export default MyApp
like image 42
yellow_space_boots Avatar answered Oct 21 '22 08:10

yellow_space_boots


Even though this is the topic Layout being mounted again and again, the root cause of this problem is that you have some data loaded in some child component which is getting fetched again and again.

After some fooling around, I found none of these problem is actually what Next.Js or SWR solves. The question, back to square one, is how to streamline a single copy of data to some child component.

Context

Use context as a example.

Config.js

import { createContext } from 'react'
export default createContext({})

_App.js

import Config from '../Config'

export default function App({ Component, pageProps }) {
  return (
    <Config.Provider value={{ user: { name: 'John' }}}>
      <Component {...pageProps} />
    </Config.Provider>
  )
}

Avatar.js

import { useContext } from 'react'
import Config from '../Config'

function Avatar() {
  const { user } = useContext(Config)
  return (
    <span>
      {user.name}
    </span>
  )
}

export default Avatar

No matter how you mount and dismount, you won't end up with re-render, as long as the _app doesn't.

Writable

The above example is only dealing with readable. If it's writable, you can try to pass a state into context. setUser will take care the set in consumer.

  <Provider value={useState({})} />
  const [user, setUser] = useContext(Config)

setUser is "cached" and won't be updated. So we can use this function to reset the user anytime in child consumer.


There're other ways, ex. React Recoil. But more or less you are dealing with a state management system to send a copy (either value or function) to somewhere else without touching other nodes. I'll leave this as an answer, since even we solved Layout issue, this problem won't disappear. And if we solve this problem, we don't need to deal with Layout at all.

like image 1
windmaomao Avatar answered Oct 21 '22 07:10

windmaomao