如何将数据从布局传递到 Next.js 应用程序文件夹中的子级?

Mau*_*ira 11 javascript reactjs next.js

我遇到的一个常见用例是在布局中使用一个按钮来切换子项中的某些内容。例如,您可以在持久布局中设置一个通知按钮,该按钮会在主应用程序中打开侧窗格。

理想情况下,您可以将isNotificationsPaneOpen状态传递给子级并让他们渲染窗格。然而,Next 13 beta 文档说这是不可能的:

在父布局与其子布局之间传递数据是不可能的。但是,您可以在一个路由中多次获取相同的数据,React 会自动删除请求的重复数据,而不会影响性能。

在后端保持这种切换状态似乎是一种重大的矫枉过正,而且感觉不自然。后端不必知道前端打开或关闭通知面板。

在我看来,这是一个足够常见的用例,人们经常会遇到这个问题。

人们通常应该如何看待这一问题?

具体例子:

// in layout.tsx

export default function NavbarLayout({
  children, 
}: {
  children: React.ReactNode,
}) {
  const [isNotificationsPaneOpen, setIsNotificationsPaneOpen]= useState(false);
  return (
    <div>
      <nav>
        <button onClick={()=>setIsNotificationsPanelOpen((prevValue)=>!prevValue)}>Notifications</button>
      </nav>
      {/* How do I pass isNotificationsPaneOpen to children? */}
      {children}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

所以在页面中:

// in page.tsx
interface PageProps {
  isNotificationsPaneOpen: boolean;
}

function Page({isNotificationsPaneOpen}:PageProps):JSX.Element {
    // render something conditionally with isNotificationsPaneOpen
    ...
}

Run Code Online (Sandbox Code Playgroud)

目前的解决方案

  1. 包括在布局中切换的窗格(IMO 的最佳候选)

如果依赖于布局状态的组件在页面之间保持相同(如通知面板示例),则将其包含在此处是有意义的。但其他示例(例如在布局中切换亮/暗模式)在每个页面上呈现不同的内容,因此这不是通用的解决方案。

  1. 外部存储喜欢localstorage传递数据

更多的是黑客,但您可以让布局将数据发送到外部数据源(最好在浏览器内),然后从主应用程序查询该数据源以确定面板是否应该打开。

You*_*mar 14

我认为您的问题是由于layoutappNext.js 的路由器(目录)中 a 的含义产生了误解13。它应该只是一个路线段( 、 等)的“在多个页面之间共享的 UI” 。page.jsloading.js

而且,正如您提到的,他们说:

在父布局与其子布局之间传递数据是不可能的。但是,您可以在一个路由中多次获取相同的数据,React 会自动删除请求的重复数据,而不会影响性能。

这是因为 alayout不是全局状态提供者。为此,需要考虑使用已知的技术,例如context. 下面是共享主题的示例:

// app/theme-provider.js

'use client';

import { createContext, useContext } from 'react';

const ThemeContext = createContext();

export function useThemContext(){
   return useContext(ThemeContext);
}

export default function ThemeProvider({ children }) {
  return (
    <ThemeContext.Provider value="dark">
      {children}
    </ThemeContext.Provider>
  );
}
Run Code Online (Sandbox Code Playgroud)
// app/layout.js

import ThemeProvider from './theme-provider';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}
Run Code Online (Sandbox Code Playgroud)
// app/component.js

"use client";

import { useThemContext } from "./theme-provider";

export default function Component() {
  const theme = useThemContext();

  // ...
}
Run Code Online (Sandbox Code Playgroud)