Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is nextjs persistent layout re-rendering all the time?

I am trying to follow the persistent layout examples as presented on the official docs and Adam Wathan's article here.

This is what I know so far:

  • I am aware about React's reconciliation process. I know that if react realizes the virtual dom tree of a component hasn't changed, then it wont update the html dom elements of that tree/component. Article I used to better understand some of these concepts
  • React rerenders a component if its state changes. If a prop changes, it should not? Or is there an assumption that a prop is implicitly considered a state?
  • If a parent re-renders, then children will be re-rendered.
  • I am aware (though still need to learn/readup) on React.memo. Once I do, I plan to utilize that as well. I am vaguely aware that it caches the component for the given input (props) and if props doesn't change, it returns the cached component.

Based on the above, I would say that persistent layout works because the layout used in _app.js is provided the page as its prop (children). Since layout's own state doesn't change, layout shouldn't get re-rendered. However, that is not what I am noticing, and hence this long winded question.

Just so I am clear, when i say re-render, I am talking about React recreating the virtual dom for the component rather than repainting the html dom. My issues are with the former and not the later.

What I am seeing is that every time I click on the "Profile" link (even if I am already on the same page):

  1. The entire layout (including top nav bar, icons, search bar and links) all re-render.
  2. I see the console log messages being printed for each of them.
  3. I used the "Profiler" tool and it too shows me all the components rerendering.

I thought that a persistent layout meant that it wouldn't be re-evaluated all the time? The printing of console logs indicates that the component is being re-evaluated every time. I know React.memo would avoid this entirely, but then what exactly is "persistence" about this? What am i missing or failing to understand about persistent layouts in this case?

What I have looks like this:

/pages/profile.js (and similarly /pages/anotherPage.js)

function sampleProfilePage (props) = {
   return (
      <div>I am on profile page</div>
   );
}
export default sampleProfilePage

_app.js

function MyApp({Component, pageProps}) {
   return (
      <SimpleLayout>
         <Component {...pageProps} />
      <SimpleLayout />
   );
}

SimpleLayout.js

function SimpleLayout ({children}) {
    return (
        <>
            {console.log("simpleLayout re-rendered")}
            <SimpleTopBar />
            <main>{children}</main>
        </>
    );
}
export default SimpleLayout;

SimpleTopbar.js NOTE: css can be ignored. Its present in .module.css file.

function SimpleTopBar () {
    return (
        <div className={classes.container}>
            {console.log("SimpleTopBar re-rendered")}
            <Link href="/profile">Profile</Link>
            <IconCircle />
            <SearchBar />
            <IconSquare />
            <Link href="/anotherPage">Another Page</Link>
        </div>
    );
}

export default SimpleTopBar;

IconCircle (and similarly IconSquare) NOTE: ignore css again. Also, I recently became aware that defaultProps are deprecated. I am in the process of updating/writing inline default values.

export function IconCircle (props) {
    return (
        <svg xmlns="http://www.w3.org/2000/svg"
            className={props.name}
            ... rest of svg data ...
        </svg>
    );
}
IconCircle.defaultProps = {
    name: classes.iconCircle
}

SearchBar.js NOTE: this is taken straight from Adam's code in order to try and compare what I was seeing.

function SearchBar (props) {
    return (
        <div className="mt-2">
            {console.log("Search bar rendered")}
            <input className="block w-full border border-gray-300 rounded-lg bg-gray-100 px-3 py-2 leading-tight focus:outline-none focus:border-gray-600 focus:bg-white"
                    placeholder="Search..."
            />
        </div>
    );
}

export default SearchBar;

Disclaimers: I am a backend engineer and just starting to learn React and Nextjs. Its is highly possible that my design and understanding is limited or not exactly what one might expect in the industry/professionally. So, if there are some general practices or commonly known knowledge, please do not assume that I am following that or aware of it. Its part of the reason why I pasted entire functions. I am still reading up on various pages/questions and trying various things to rule things out, or understand better what is being shown/told to me.

Thank you in advance for the patience to read this question, and sorry for its length.

like image 928
itsonlyme Avatar asked Apr 09 '26 13:04

itsonlyme


1 Answers

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.

The layouts will 'persist' between pages - the children should rerender on a route change but, components that are still on the page should keep their state.

i.e. a search input in the layout should keep its search term on route changes to another page with the same layout.

The only way not to rerender during route change is to use shallow routing . But it doesn't really route - it just allows you to add query params to the current route (can't change pages or it will use standard routing).

As you mentioned, you can use memo on some of your components to prevent rerendering, but only use it when you know you need it and use it wisley.

Lastly, rerendering is also part of React and virtual DOM manipulation, I wouldn't worry about it too much until it becomes a problem.

like image 112
Sean W Avatar answered Apr 11 '26 04:04

Sean W



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!