wagmi cookieToInitialState 详解:SSR 场景下从 Cookie 恢复连接状态的实用工具 📅 发布时间:2026/9/17 5:57:34 👁 浏览次数: wagmi cookieToInitialState 详解SSR 场景下从 Cookie 恢复连接状态的实用工具【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmicookieToInitialState是 wagmi 提供的一个服务端辅助函数用于把浏览器随请求携带的 Cookie 字符串解析为 Wagmi Config 的initialState从而在 SSR如 Next.js场景下让服务端渲染的页面与客户端水合后的状态保持一致避免未连接状态闪屏。读完本文你将理解该函数的签名与参数语义、它在 SSR 数据流中的位置以及它在 packages/core/src/utils/cookie.ts 中的真实实现细节与容错行为。一、它解决什么问题SSR 与客户端持久化存储的矛盾Wagmi 默认使用localStorage这类客户端专属的外部存储来展示用户最近连接过的钱包等数据。但在 Next.js 等带 SSR 的框架中服务端渲染的 HTML 无法读取localStorage这会导致服务端 HTML 与客户端水合后的 HTML 不一致触发 hydration 警告。对此SSR 指南给出的第一步方案是在 Config 中开启ssr: true让外部存储的内容在客户端首次挂载后再水合。但开启ssr之后还有一个问题在首次挂载完成前用户仍会看到空数据例如账户显示disconnected而非reconnecting、地址为空而非上次连接的地址。为了让状态在服务端与客户端之间传递指南推荐的方案是使用 Cookie 持久化在 Wagmi Config 中把存储切换为cookieStorageCookie 会随每次 HTTP 请求自动发送到服务端在服务端请求处理器中调用cookieToInitialState从请求头里的 Cookie 字符串还原出initialState把还原结果传给 WagmiProvider 的initialState属性使服务端渲染直接使用上一次的连接状态。cookieToInitialState正是这条链路中服务端的核心一环它的官方定义见 site/shared/utilities/cookieToInitialState.md是Helper to convert a cookie string into initial state。二、导入与函数签名导入import { cookieToInitialState } from wagmi参数参数类型说明configConfig你的 Wagmi Config函数依赖其中的storage.key来定位 Cookie 中存放状态的键名cookiestring \| null \| undefined完整的 Cookie 字符串通常取自请求头如req.headers.cookie或 Next.js 的headers().get(cookie)返回值类型说明State \| undefined解析出的初始状态当 Cookie 不存在、找不到对应键或反序列化失败时返回undefined此时WagmiProvider会回退到默认初始状态三、完整用法1. 服务端从请求头还原状态官方示例展示了一个通用服务端 handler 的用法// server.ts import { cookieToInitialState } from wagmi import config from ./config function handler(req: Request) { const initialState cookieToInitialState(config, req.headers.cookie) // ... }2. 客户端 Config必须使用 cookieStoragecookieToInitialState依赖 Config 中的存储键名因此客户端 Config 需要把底层存储配置为cookieStorage该函数与 createStorage 默认键名wagmi配合读取的 Cookie 键为wagmi.store// config.ts import { createConfig, http, cookieStorage, createStorage, } from wagmi import { mainnet, sepolia } from wagmi/chains export const config createConfig({ chains: [mainnet, sepolia], ssr: true, storage: createStorage({ storage: cookieStorage, }), transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })3. Next.js App Router 中的落地示例SSR 指南给出了完整的 Next.js App Directory 接线方式在app/layout.tsxServer Component中提取 Cookie 并调用cookieToInitialState把结果经 Props 传入位于 Client Component 中的WagmiProvider// app/layout.tsx import { type ReactNode } from react import { headers } from next/headers import { cookieToInitialState } from wagmi import { getConfig } from ./config import { Providers } from ./providers export default async function Layout({ children }: { children: ReactNode }) { const initialState cookieToInitialState( getConfig(), (await headers()).get(cookie), ) return ( html langen body Providers initialState{initialState}{children}/Providers /body /html ) }// app/providers.tsx use client import { QueryClient, QueryClientProvider } from tanstack/react-query import { type ReactNode, useState } from react import { type State, WagmiProvider } from wagmi import { getConfig } from ./config type Props { children: ReactNode initialState: State | undefined, } export function Providers({ children, initialState }: Props) { const [config] useState(() getConfig()) const [queryClient] useState(() new QueryClient()) return ( WagmiProvider config{config} initialState{initialState} QueryClientProvider client{queryClient}{children}/QueryClientProvider /WagmiProvider ) }注意WagmiProvider必须放在带use client的组件中initialState类型为State | undefined因此传入cookieToInitialState的返回值可能为undefined在类型上是天然兼容的。四、源码级实现剖析4.1 核心实现三行主逻辑 双重容错cookieToInitialState的实现位于 packages/core/src/utils/cookie.ts逻辑非常短export function cookieToInitialState(config: Config, cookie?: string | null) { if (!cookie) return undefined const key ${config.storage?.key}.store const parsed parseCookie(cookie, key) if (!parsed) return undefined try { return deserialize{ state?: State } | null(parsed)?.state } catch { return undefined } }从中可以确认几个关键行为Cookie 键名由 Config 推导读取的键是${config.storage?.key}.store。createStorage的key前缀默认为wagmi见 packages/core/src/createStorage.ts所以默认情况下读取的是wagmi.store如果你自定义了存储前缀cookieToInitialState会自动跟随无需手动指定。name: store与 Config 内部持久化机制对应在 packages/core/src/createConfig.ts 中Config 使用 zustand 的persist中间件持久化状态且name: store——这正是 Cookie 键后缀.store的来源。partialize只持久化关键字段以控制存储体积connections转换为__type: Map的可序列化形式、chainId和current。这也解释了为什么还原出的State只包含这三项。三重undefined兜底没有 Cookie、Cookie 中找不到目标键、反序列化抛错如值损坏三种情况都会安全地返回undefined而不会把异常抛给上层框架。取的是parsed?.state而非整个值反序列化后的对象结构是{ state: State, version: number }redux/zustand persist 的标准信封格式函数只取出其中的state字段返回。4.2 Cookie 解析parseCookie底层键值提取由同文件的 parseCookie 完成export function parseCookie(cookie: string, key: string) { const keyValue cookie.split(; ).find((x) x.startsWith(${key})) if (!keyValue) return undefined return keyValue.substring(key.length 1) }它以; 为分隔符切分整段 Cookie 头再按key前缀匹配目标条目因此能正确处理同一请求头中混有多个 Cookie如foobar; wagmi.store...的情况。4.3 反序列化Map 与 BigInt 的还原deserialize定义在 packages/core/src/utils/deserialize.ts它通过JSON.parse的 reviver 回调把序列化时的类型标记还原为原生对象export function deserializetype(value: string, reviver?: Reviver): type { return JSON.parse(value, (key, value_) { let value value_ if (value?.__type bigint) value BigInt(value.value) if (value?.__type Map) value new Map(value.value) return reviver?.(key, value) ?? value }) }其中__type: Map的还原至关重要——Config 持久化时把connections这个Map编码成了{ __type: Map, value: [...] }cookieToInitialState返回前会把它还原成真正的Map实例供WagmiProvider直接水合进 store。4.4 cookieStorage写入侧实现与cookieToInitialState读取侧配套的写入侧是 cookieStorageexport const cookieStorage { getItem(key) { if (typeof window undefined) return null const value parseCookie(document.cookie, key) return value ?? null }, setItem(key, value) { if (typeof window undefined) return // biome-ignore lint/suspicious/noDocumentCookie: using document.cookie ${key}${value};path/;samesiteLax }, removeItem(key) { if (typeof window undefined) return // biome-ignore lint/suspicious/noDocumentCookie: using document.cookie ${key};max-age-1;path/ }, } satisfies BaseStorage三个方法都在服务端无window时静默降级为 no-op保证同一份代码在 SSR 环境下不会抛错写入时固定path/; samesiteLax删除则通过把max-age置为负数清除。五、测试用例对行为的验证packages/core/src/utils/cookie.test.ts 中的cookieToInitialState测试第 23-47 行用快照完整锁定了上述行为边界test(cookieToInitialState, () { const config createConfig({ chains: [mainnet], transports: { [mainnet.id]: http() }, storage: createStorage({ storage: cookieStorage }), }) expect( cookieToInitialState( config, wagmi.store{state:{connections:{__type:Map,value:[]},chainId:1,current:null},version:2}; , ), ).toMatchInlineSnapshot( { chainId: 1, connections: Map {}, current: null, } ) expect(cookieToInitialState(config)).toMatchInlineSnapshot(undefined) expect(cookieToInitialState(config), foo).toMatchInlineSnapshot(undefined) expect(cookieToInitialState(config, wagmi.storeinvalid; )).toBeUndefined() expect(cookieToInitialState(config, wagmi.storenull; )).toBeUndefined() })从快照可以读出四条事实一个合法的wagmi.storeCookie 被还原为{ chainId: 1, connections: Map {}, current: null }——注意connections是Map实例而非普通对象印证了 4.3 节的类型还原逻辑未传 Cookie、传无关 Cookiefoo、值非法wagmi.storeinvalid、值为字面量null时均返回undefined与 4.1 节的容错设计一一对应。同文件的parseCookie测试还验证了多 Cookie 混合场景下仍能精确提取目标键值。六、使用注意事项必须与ssr: true搭配只有开启ssr后Config 才会在客户端挂载后水合外部存储此时initialState才有消除首屏空状态闪屏的意义背景见 site/react/guides/ssr.md。客户端与服务端必须使用同一个 Config 构造Cookie 键由config.storage?.key派生若服务端还原状态用的 Config 前缀与客户端写入 Cookie 的 Config 不一致将永远解析不到wagmi.store。返回undefined是合法且常见状态首次访问无 Cookie或 Cookie 损坏时都会得到undefined将其直接传给WagmiProvider的initialState即可Provider 会回退到默认初始状态。状态体积受partialize约束Cookie 中只包含connections、chainId、current三个关键字段见 packages/core/src/createConfig.ts 的partialize实现不会持久化status等易变字段避免重连逻辑被脏数据干扰。适用框架不限于 Next.js函数本身只依赖(config, cookie 字符串)两个输入Nuxt、Vanilla SSR 等任何能拿到请求 Cookie 头的服务端场景同样适用只是各框架提取 Cookie 的方式不同。参考文件文档site/react/api/utilities/cookieToInitialState.md原文为 site/shared/utilities/cookieToInitialState.md 的 React 侧引用实现packages/core/src/utils/cookie.ts、packages/core/src/utils/deserialize.ts、packages/core/src/createStorage.ts、packages/core/src/createConfig.ts测试packages/core/src/utils/cookie.test.ts配套指南site/react/guides/ssr.md、site/react/api/WagmiProvider.md【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考