admin管理员组

文章数量:1194134

A common use case I run across is having a button in a layout toggle something in the children. For example, you could have a notifications button in a persisted layout that opens a side pane in the main app.

Ideally, you would be able to pass an isNotificationsPaneOpen state down to the children and have them render the pane. However,the Next 13 beta docs say this isn't possible:

Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests without affecting performance.

Persisting this toggle state in the backend seems like a major overkill and feels unnatural. The backend shouldn't have to know about the frontend opening or closing a notifications panel.

It seems to me that this is a common enough use case that people will run into this issue often.

How should one generally think about this?

Concrete example:

// 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>
  );
}

So that in the page:

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

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

Current solutions

  1. Include the pane to toggle in the layout (best candidate IMO)

If the component that depends on the layout's state remains the same across pages (like the notifications panel example), it makes sense to include it here. But other examples - like toggling light/dark mode in the layout - render different things on each page so this is not a general solution.

  1. External storage like localstorage to pass data

More of a hack but you could have the layout send data to an external data source (ideally within the browser) and then query that data source from the main app to determine if the panel should be open or not.

A common use case I run across is having a button in a layout toggle something in the children. For example, you could have a notifications button in a persisted layout that opens a side pane in the main app.

Ideally, you would be able to pass an isNotificationsPaneOpen state down to the children and have them render the pane. However,the Next 13 beta docs say this isn't possible:

Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests without affecting performance.

Persisting this toggle state in the backend seems like a major overkill and feels unnatural. The backend shouldn't have to know about the frontend opening or closing a notifications panel.

It seems to me that this is a common enough use case that people will run into this issue often.

How should one generally think about this?

Concrete example:

// 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>
  );
}

So that in the page:

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

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

Current solutions

  1. Include the pane to toggle in the layout (best candidate IMO)

If the component that depends on the layout's state remains the same across pages (like the notifications panel example), it makes sense to include it here. But other examples - like toggling light/dark mode in the layout - render different things on each page so this is not a general solution.

  1. External storage like localstorage to pass data

More of a hack but you could have the layout send data to an external data source (ideally within the browser) and then query that data source from the main app to determine if the panel should be open or not.

Share Improve this question edited May 1, 2023 at 9:19 Youssouf Oumar 45.8k16 gold badges98 silver badges103 bronze badges asked Jan 25, 2023 at 19:29 MauroNeiraMauroNeira 4451 gold badge4 silver badges15 bronze badges 1
  • Can do it with React.createElement as well, I guess? – Arpit Bhalla Commented May 1, 2023 at 9:25
Add a comment  | 

1 Answer 1

Reset to default 22

I think your question is the result of a misconception of what a layout is for within the app router (directory) of Next.js 13. It's supposed to be just an "UI that is shared between multiple pages" of a route segment (page.js, loading.js, etc.).

And, as you mentioned, they say:

Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests without affecting performance.

This is because a layout is not a global state provider. For this need consider using already known technics, for example, a context. Find below an example of sharing a theme:

// 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>
  );
}
// app/layout.js

import ThemeProvider from './theme-provider';

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

"use client";

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

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

  // ...
}

本文标签: javascriptHow to pass data from a layout to the children in the app folder of NextjsStack Overflow