Next.js App Router 状态共享实战:在 share-state 示例中掌握 Layout 与页面间的 Context 共享
Next.js App Router 状态共享实战在 share-state 示例中掌握 Layout 与页面间的 Context 共享【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples本篇技术指南以仓库中的 share-state 示例 为蓝本讲解如何在 Next.js App Routerapp 目录下跨 Layout 与各级页面共享 React 状态。读完你将掌握一套可复用的「客户端 Layout 服务端页面 Context Provider」模式以及通过路由组Route Group让根 Layout 保持服务端能力、仅在局部开启客户端共享的进阶方案。示例背景与要解决的问题在 Next.js App Router 中页面Page与布局Layout既可以是服务端组件也可以是客户端组件。当页面需要从服务端拉取数据同时又需要在 Layout 中维护交互状态例如跨嵌套路由共享的计数器、用户偏好、购物车数量时单纯依赖各页面内部useState会带来两个痛点状态只存在于单个页面组件内部无法被 Layout 或兄弟/嵌套路由读取Layout 一旦需要渲染状态就必须成为客户端组件从而失去在服务端渲染子树的能力。share-state示例通过 React Context 将状态提升到 Layout 层并利用「客户端组件不能包含服务端组件但客户端 Layout 可以渲染来自服务端的页面」这一组合实现了状态在整棵路由树中的共享。在线效果可参考示例 app/page.tsx 中给出的 demo 说明。快速开始两种方式运行示例仓库 README 提供了两种使用方式均可直接从本仓库的app-directory/share-state目录出发。方式一一键部署到 Vercel使用 Vercel 的一键部署按钮直接以本示例为模板创建新项目。这种方式无需本地安装依赖适合先快速查看运行效果。方式二克隆后本地运行使用create-next-app配合 pnpm 从本示例目录初始化项目pnpm create next-app --example https://github.com/vercel/examples/tree/main/app-directory/share-state初始化完成后启动开发服务器pnpm dev之后可以执行pnpm build进行生产构建、pnpm start启动生产服务器。以上脚本定义在 package.json 中。需要说明的是该示例声明了engines.node: 22.x并基于next^16、react^19以及 UI 依赖vercel/examples-ui运行前请确认本地 Node 版本匹配。核心模式客户端 Layout 服务端页面示例首页 app/page.tsx 明确阐述了本模式的关键思想尽管客户端组件不能包含服务端组件但我们可以让一个客户端 Layout 去渲染来自服务端的页面。这样我们就能创建一个状态或 Context在整个应用中共享数据。这一约束决定了目录结构的设计负责展示状态的 Layout 和提供状态的 Provider 必须是客户端组件use client而各页面仍保持服务端组件Server Component页面负责渲染可交互的 Counter 客户端组件来变更共享状态。README 中给出的标准文件结构如下/app ├── layout.js (client) # 根布局展示计数器状态 ├── providers.js (client) # 导出 counter context ├── counter.js (client) # 更新计数器的组件 ├── page.js (server) # 渲染 counter 组件 └── nested └── page.js (server) # 嵌套页面同样渲染 counter 组件第一步创建共享 Context 与 Hook状态共享的第一步是创建一个 Context内部存储counter值并导出一个useCounterHook 供组件读取与修改。README 中的简化版本如下// app/providers.js import { createContext, useContext, useState } from react const Counter createContext([0, () {}]) function CounterProvider({ children }) { const state useState(0) return Counter.Provider value{state}{children}/Counter.Provider } const useCounter () useContext(Counter) export { CounterProvider, useCounter }这里的createContext([0, () {}])同时定义了上下文的默认值计数0与空操作函数CounterProvider内部直接使用useState(0)的返回值即[counter, setCounter]元组作为 Context 的 valueuseCounter则是对useContext的一层封装让消费方无需关心 Context 对象本身。在仓库的实际 TypeScript 实现中app/demo/providers.tsx相同的逻辑被补全了完整类型import { createContext, useContext, useState, type ReactNode, type Dispatch, type SetStateAction, } from react type CounterContext [number, DispatchSetStateActionnumber] const Counter createContextCounterContext([0, () {}]) function CounterProvider({ children }: { children: ReactNode }) { const state useState(0) return Counter.Provider value{state}{children}/Counter.Provider } const useCounter () useContext(Counter) export { CounterProvider, useCounter }可以看到CounterContext被显式声明为[number, DispatchSetStateActionnumber]元组类型这正是 ReactuseState返回值的类型保证了 Provider 与消费端在使用上的类型安全。第二步用 Provider 包裹根 Layout 并消费状态接下来把根 Layout 用CounterProvider包裹使useCounter对其下的所有页面可用同时在 Layout 中直接消费counter值进行展示。README 中的简化版本如下// app/layout.js use client import { Link } from next/link import { CounterProvider, useCounter } from ./providers function RootLayout({ children }) { const [counter] useCounter() return ( nav Link href/Index/Link Link href/nestedNested/Link /nav {children} hr / h2Counter/h2 p{counter}/p / ) } export default function RootLayoutContainer(props) { return ( CounterProvider RootLayout {...props} / /CounterProvider ) }这里有一个值得注意的架构细节RootLayout是use client客户端组件但它接收并透传childrenchildren是由服务端渲染的页面子树。这正是「客户端 Layout 渲染服务端页面」的核心——客户端组件不能在自己的模块作用域内导入服务端组件但作为children传入的子树仍可由服务端渲染后注入。仓库的实际实现app/demo/layout.tsx结构与上面一致只是在导航与计数展示上使用了vercel/examples-ui的Page、Link、Text组件并额外提供了一个「← Back to example」返回链接use client import { Link, Page, Text } from vercel/examples-ui import { CounterProvider, useCounter } from ./providers interface Props { children: React.ReactNode } function RootLayout({ children }: Props) { const [counter] useCounter() return ( Page classNameflex flex-col gap-12 nav classNameflex gap-4 Link href/demoIndex/Link Link href/demo/nestedNested/Link /nav {children} section classNameflex flex-col gap-3 Text varianth2Counter/Text Text classNametext-3xl font-black{counter}/Text /section section classNameflex flex-col gap-3 Link href/← Back to example/Link /section /Page ) } export default function RootLayoutContainer(props: Props) { return ( CounterProvider RootLayout {...props} / /CounterProvider ) }注意这里使用了「容器组件」模式默认导出的RootLayoutContainer负责用 Provider 包裹真正渲染布局的RootLayout在 Provider 内部消费useCounter()确保每次渲染都能拿到最新的 Context 值。第三步创建可交互的 Counter 组件Counter 组件负责增减计数值它通过useCounter同时拿到counter和setCounter。README 中的简化版本如下// app/counter.js use client import { useCounter } from ./providers export default function Counter() { const [counter, setCounter] useCounter() return ( div button onClick{() setCounter((counter) counter - 1)} Decrement /button p{counter}/p button onClick{() setCounter((counter) counter 1)} Increment /button /div ) }仓库的实际实现app/demo/counter.tsx使用vercel/examples-ui的Button与Text组件逻辑完全一致。setCounter采用函数式更新(counter) counter - 1/(counter) counter 1这是基于上一次状态的安全写法可避免闭包过期问题。第四步创建页面与嵌套页面最后创建普通页面和嵌套页面两者都渲染同一个Counter组件。由于这两个页面都是服务端组件它们各自负责在服务端渲染 Counter而 Counter 内部的状态读写则经由 Context 指向同一个 Provider——因此无论用户在 Index 页还是 Nested 页点击按钮共享的计数值都会同步反映到 Layout 中。README 中的两个页面实现// app/page.js import Counter from ./counter export default function IndexPage() { return ( div h1Index page/h1 Counter / /div ) }// app/nested/page.js import Counter from ./counter export default function NestedPage() { return ( div h1Nested page/h1 Counter / /div ) }仓库中的对应实现分别为 app/demo/page.tsx 与 app/demo/nested/page.tsx。从源码结构可以看到实际文件树app/demo/ ├── providers.tsx # 导出 CounterProvider 与 useCounter ├── layout.tsx # 客户端布局展示共享计数 ├── counter.tsx # 增减计数组件 ├── page.tsx # Index 页服务端组件 └── nested/ └── page.tsx # Nested 页服务端组件而 app/layout.tsx 是真正的根 Layout服务端组件它通过vercel/examples-ui的Layout组件与getMetadata提供页面元信息并把app/demo子树交给上面这套客户端共享方案处理从而形成了「服务端根布局 客户端局部布局」的分层结构。进阶需要从 Layout 拉取服务端数据时怎么办如果根 Layout 自身需要从服务端获取数据直接把它变成客户端组件会丢失服务端能力。README 给出的解法是创建一个路由组Route Group把客户端共享 Layout 放进组内根 Layout 保持服务端组件不变。调整后的文件结构/app ├── layout.js (server) # 根布局保持服务端能力可拉取数据 └── (app) ├── layout.js (client) # 组内布局客户端承载 Context ├── page.js (server) # 组内页面 ├── providers.js (client) ├── counter.js (client) └── nested └── page.js (server)路由组用括号包裹的目录如(app)不会产生 URL 路径段但可以拥有独立的 Layout。这样根 Layout 继续执行服务端数据获取组内客户端 Layout 负责状态共享组内页面包括嵌套路由则同时受益于两者——既能在服务端获取数据又能在 Layout 与路由之间共享交互状态。运行环境与配置说明示例的 next.config.js 中启用了experimental.appDir/** type {import(next).NextConfig} */ module.exports { experimental: { appDir: true, }, }这是 App Router 早期阶段的显式开关在较新版本的 Next.js 中 App Router 已为默认特性该配置项主要用于兼容旧版本。结合 package.json 中next: ^16.0.10、react: ^19.2.1、react-dom: ^19.2.1的依赖声明与node: 22.x的引擎约束建议在对应的新版本环境下运行本示例以获得最佳兼容性。小结通过share-state示例可以提炼出一套可复用的 App Router 状态共享模式用providers.tsx集中创建 Context 与useXxxHookProvider内部持有useState在客户端 Layout 中用容器组件包裹 Provider并在 Layout 内直接消费状态页面保持服务端组件仅渲染导入的交互组件状态读写全部经由 Context 汇聚到同一 Provider当根 Layout 需要服务端数据时用路由组把「客户端共享 Layout」下沉到组内实现服务端能力与客户端共享的兼得。这一模式不局限于计数器场景任何需要跨 Layout 与嵌套路由共享的客户端状态如主题、购物车、筛选条件都可以按同样的结构落地。【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

论文生成软件怎么选?DeepSeek V3.5 的模型通道改到 TaoToken

论文生成软件怎么选?DeepSeek V3.5 的模型通道改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/18 22:30:42 阅读更多 →
BERT模型原理与实战:从NLP基础到应用优化

BERT模型原理与实战:从NLP基础到应用优化

1. 项目概述今天咱们来聊聊自然语言处理(NLP)这个让机器"读懂"人类语言的神奇领域,特别是近年来大火的BERT模型。作为一名在NLP领域摸爬滚打多年的从业者,我见证了从传统机器学习方法到深度学习,再到Transformer架构的革命性突破。…

2026/9/18 22:30:42 阅读更多 →
2026毕业AI论文工具红黑榜|实测避雷!别再乱用工具挂科

2026毕业AI论文工具红黑榜|实测避雷!别再乱用工具挂科

2026年高校论文重复率AIGC双重审核全面落地,很多同学论文查重合格,却因为AI痕迹超标、文献造假、格式不规范直接退回重写。市面上AI论文工具五花八门,通用AI、国外科研工具、单一查重工具各有短板,盲目使用只会耽误毕业进度。今天…

2026/9/18 22:30:42 阅读更多 →

最新新闻

如何用VimWiki构建标签分类体系:Tags完全指南

如何用VimWiki构建标签分类体系:Tags完全指南

如何用VimWiki构建标签分类体系:Tags完全指南 【免费下载链接】vimwiki Personal Wiki for Vim 项目地址: https://gitcode.com/GitHub_Trending/vi/vimwiki VimWiki 是一款运行在 Vim 里的个人 Wiki 插件,除了双向链接和日记外,它的 …

2026/9/18 23:15:05 阅读更多 →
AutoRAG 集成 Chroma 向量数据库实战指南:四种客户端模式、YAML 配置与检索节点调用

AutoRAG 集成 Chroma 向量数据库实战指南:四种客户端模式、YAML 配置与检索节点调用

AutoRAG 集成 Chroma 向量数据库实战指南:四种客户端模式、YAML 配置与检索节点调用 【免费下载链接】AutoRAG AutoRAG: Now your agent can find anything in your computer. It gets smarter if you are using it frequently. 项目地址: https://gitcode.com/Gi…

2026/9/18 23:15:05 阅读更多 →
Kafka、RocketMQ、RabbitMQ怎么选?从架构原理到真实场景的选型指南

Kafka、RocketMQ、RabbitMQ怎么选?从架构原理到真实场景的选型指南

做后端开发的这几年,我跟这三款消息中间件都打过不少交道。你翻社区里的选型文章,经常看到一堆对比表格,什么吞吐量几十万每秒、延迟几毫秒、支持事务消息……表格背下来了,但真到自己做技术方案时,还是不知道选哪个。…

2026/9/18 23:15:05 阅读更多 →
settings.json 写完还连 Anthropic?千问走 TaoToken 时先跑 /status

settings.json 写完还连 Anthropic?千问走 TaoToken 时先跑 /status

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/18 23:15:05 阅读更多 →
LangChain V1.0构建高效RAG系统实战指南

LangChain V1.0构建高效RAG系统实战指南

1. 项目概述在自然语言处理领域,检索增强生成(Retrieval-Augmented Generation,简称RAG)正逐渐成为连接大型语言模型与领域知识的关键桥梁。LangChain作为当前最流行的LLM应用开发框架,其1.0版本对RAG模块进行了全面升…

2026/9/18 23:15:05 阅读更多 →
青海专业网页设计免费建站避坑指南3大技术选型注意事项

青海专业网页设计免费建站避坑指南3大技术选型注意事项

青海专业网页设计免费建站避坑指南3大技术选型注意事项 网站做好了没人访问,这简直是无数中小企业主和创业者的噩梦。你花钱请人做了个精美的官网,或者自己折腾了半天用免费模板搭了个站,结果打开一看,后台流量数据惨不忍睹,连个像样的询盘都没有。这时候你才意识到,问题根本不在美工把图片调得有多亮,而在于你从一…

2026/9/18 23:14:23 阅读更多 →

日新闻

Matlab手写逻辑回归:从数学原理到多变量概率预测模型实现

Matlab手写逻辑回归:从数学原理到多变量概率预测模型实现

很多朋友第一次看到"逻辑回归"这四个字,第一反应就是——这玩意儿是个回归模型吧?我当年也是在Matlab里跑完一段代码,看着输出的0.73、0.86这种概率值,才回过神来:这家伙其实是披着回归外衣的分类神器&#…

2026/9/18 0:00:28 阅读更多 →
高值医用耗材研报PDF:用Python完成字段抽取、清洗与趋势预测

高值医用耗材研报PDF:用Python完成字段抽取、清洗与趋势预测

简介:这份报告是2023-2028年高值医用耗材行业调研及发展前景趋势预测报告,面向医疗器械企业管理者、投资机构、行业研究人员及关注政策变化的从业者,用于把握行业监管动向、市场格局与未来趋势。报告以PDF格式呈现,共1个文件、整体…

2026/9/18 0:00:28 阅读更多 →
三维高斯场赋能世界模型:几何语义蒸馏与机器人决策实战

三维高斯场赋能世界模型:几何语义蒸馏与机器人决策实战

先把我自己的背景交代一下:我之前在搞具身智能和机器人导航相关的项目,很长一段时间里都被“环境表示”这件事卡着。传统做法是用点云或者网格做几何建模,语义信息另外再跑分割模型,两套东西各管各的,时间一长就会发现…

2026/9/18 0:00:28 阅读更多 →

周新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/16 19:03:19 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/17 7:57:36 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/17 10:19:14 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/16 22:31:27 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/15 21:39:18 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/16 22:32:59 阅读更多 →