Next.js 13基于文件夹路由中,如何从layout.js向page.js传递props?
解决方案:Next.js 13 布局向子页面传递状态
核心问题原因
在Next.js 13 App Router中,children并非单个React元素,而是React节点数组(或流式渲染内容),直接用React.cloneElement处理会失效——因为它只能操作单个元素。
正确实现方式
方法1:使用React Context(适配文件夹路由结构)
这是最贴合App Router设计的方案,无需组件导入或全局状态管理,仅在当前路由组内生效:
- 在
(DropDownPages)目录下创建Context文件
// app/(DropDownPages)/MenuContext.js "use client"; import { createContext, useContext, useState } from "react"; const MenuContext = createContext(); export function MenuProvider({ children }) { const [selectedMenu, setSelectedMenu] = useState(null); const handleTitleClick = (menu) => { setSelectedMenu(menu); }; return ( <MenuContext.Provider value={{ selectedMenu, handleTitleClick }}> {children} </MenuContext.Provider> ); } export function useMenu() { const context = useContext(MenuContext); if (!context) { throw new Error("useMenu必须在MenuProvider范围内使用"); } return context; }
- 修改父布局
app/(DropDownPages)/layout.js
将状态逻辑移到Provider中,布局仅负责渲染UI:
"use client" import styled from "styled-components"; import { MenuProvider, useMenu } from "./MenuContext"; const 전체 = 'all' const 상품 = 'product' const 카테고리 = 'category' const 기획전 = 'exhibition' const 브랜드 = 'brand' // 省略styled-components样式代码 function CategoryLayout({ children }) { const { handleTitleClick } = useMenu(); return ( <Section> <nav className="flex justify-center items-center mt-6 mb-6"> <MenuBox onClick={() => handleTitleClick(전체)} > <ImageWrapper><StyledImage src="/전체.png" /></ImageWrapper> <Title>전체</Title> </MenuBox> <MenuBox onClick={() => handleTitleClick(상품)} > <ImageWrapper><StyledImage src="/상품.png" /></ImageWrapper> <Title>전체</Title> </MenuBox> <MenuBox onClick={() => handleTitleClick(카테고리)} > <ImageWrapper><StyledImage src="/카테고리.png" /></ImageWrapper> <Title>전체</Title> </MenuBox> <MenuBox onClick={() => handleTitleClick(기획전)} > <ImageWrapper><StyledImage src="/기획전.png" /></ImageWrapper> <Title>전체</Title> </MenuBox> <MenuBox onClick={() => handleTitleClick(브랜드)} > <ImageWrapper><StyledImage src="/브랜드.png" /></ImageWrapper> <Title>전체</Title> </MenuBox> </nav> <Main>{children}</Main> </Section> ) } export default function Category({ children }) { return ( <MenuProvider> <CategoryLayout>{children}</CategoryLayout> </MenuProvider> ); }
- 子页面接收状态(如
product/page.js)
"use client"; import styled from "styled-components"; import { useMenu } from "../MenuContext"; export default function Product() { const { selectedMenu } = useMenu(); console.log(selectedMenu); // 现在能正常获取选中菜单值 // 省略其他代码 }
方法2:遍历子元素批量传递Props
如果不想用Context,可通过React.Children.map逐个处理子元素,但注意该方案仅适用于静态子元素场景,对流式渲染兼容性较差:
修改app/(DropDownPages)/layout.js的渲染部分:
// 省略其他代码 <Main> {Children.map(children, child => cloneElement(child, { selectedMenu }) )} </Main>
子页面即可直接通过props接收selectedMenu:
export default function Product({selectedMenu}) { console.log(selectedMenu); // 正常输出选中值 }
方案对比
- Context方案:适配App Router流式渲染特性,支持复杂页面结构,是官方推荐的路由组内传值方式。
- Children.map方案:实现简单,但局限性大,仅适合静态子组件场景。
内容的提问来源于stack exchange,提问作者Lee Doyu
相关产品推荐
相关产品推荐

