为什么 nextjs 持久布局总是重新渲染?

its*_*yme 8 persistence reactjs next.js

我正在尝试遵循官方文档Adam Wathan 的文章中介绍的持久布局示例。

这是我目前所知道的:

  • 我了解 React 的协调过程。我知道如果 React 意识到组件的虚拟 dom 树没有改变,那么它就不会更新该树/组件的 html dom 元素。我用来更好地理解其中一些概念的文章
  • 如果组件的状态发生变化,React 会重新渲染组件。如果一个 prop 改变了,它不应该改变吗?或者是否有一个假设,即道具隐含地被视为一种状态?
  • 如果父级重新渲染,则子级将被重新渲染。
  • 我知道(尽管仍然需要学习/阅读)React.memo。一旦我这样做了,我也计划利用它。我隐约意识到它会缓存给定输入(props)的组件,如果 props 没有改变,它会返回缓存的组件。

基于上述内容,我想说持久布局是有效的,因为中使用的布局_app.js是提供页面作为其道具(子级)的。由于布局自身的状态不会改变,因此布局不应该被重新渲染。然而,这不是我注意到的,因此才有了这个冗长的问题。

我很清楚,当我说重新渲染时,我指的是 React 为组件重新创建虚拟 dom,而不是重新绘制 html dom。我的问题是前者,而不是后者。

我看到的是,每次我单击“个人资料”链接(即使我已经在同一页面上):

  1. 整个布局(包括顶部导航栏、图标、搜索栏和链接)全部重新渲染。
  2. 我看到每个控制台日志消息都被打印出来。
  3. 我使用了“Profiler”工具,它也向我显示了所有组件的重新渲染。

我认为持久布局意味着它不会一直被重新评估?控制台日志的打印表明该组件每次都被重新评估。我知道 React.memo 会完全避免这种情况,但是“持久性”到底是什么?在这种情况下,我对持久布局缺少或无法理解什么?

我所拥有的看起来像这样:

/pages/profile.js (以及类似的 /pages/anotherPage.js)

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

_app.js

function MyApp({Component, pageProps}) {
   return (
      <SimpleLayout>
         <Component {...pageProps} />
      <SimpleLayout />
   );
}
Run Code Online (Sandbox Code Playgroud)

SimpleLayout.js

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

SimpleTopbar.js 注意:css 可以忽略。它存在于 .module.css 文件中。

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;
Run Code Online (Sandbox Code Playgroud)

IconCircle(以及类似的 IconSquare)注意:再次忽略 css。另外,我最近意识到 defaultProps 已被弃用。我正在更新/写入内联默认值。

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
}
Run Code Online (Sandbox Code Playgroud)

SearchBar.js 注意:这是直接取自 Adam 的代码,以便尝试比较我所看到的内容。

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;
Run Code Online (Sandbox Code Playgroud)

免责声明:我是一名后端工程师,刚刚开始学习 React 和 Nextjs。我的设计和理解很可能是有限的,或者不完全符合行业/专业人士的期望。因此,如果有一些一般做法或众所周知的知识,请不要认为我正在遵循或意识到它。这是我粘贴整个函数的部分原因。我仍在阅读各种页面/问题,并尝试各种方法来排除问题,或者更好地理解向我展示/告诉我的内容。

预先感谢您耐心阅读这个问题,对于其篇幅深表歉意。

Sea*_*n W 4

你对正在发生的事情有很好的了解。

Next.js 中的所有页面都依赖于_app - 它会因为pageProps或更可能的Componentprop 更改而重新渲染 - 迫使子级重新渲染。

布局将在页面之间“保留” - 子级应该在路由更改时重新渲染,但是仍在页面上的组件应该保持其状态。

即布局中的搜索输入应将其搜索词保留在路由更改到具有相同布局的另一个页面上。

在路由更改期间不重新渲染的唯一方法是使用浅层路由。但它并不是真正的路由 - 它只是允许您将查询参数添加到当前路由(不能更改页面,否则它将使用标准路由)。

正如您所提到的,您可以在某些组件上使用 memo 来防止重新渲染,但只有在您知道需要它并使用它wisley时才使用它。

最后,重新渲染也是 React 和虚拟 DOM 操作的一部分,在它成为问题之前我不会太担心它。