ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

wagmi useClient Hook 完全指南:获取与响应式订阅 Viem Client 实例

2026/9/17 23:06:11 拓冰建站 浏览量
wagmi useClient Hook 完全指南:获取与响应式订阅 Viem Client 实例 wagmi useClient Hook 完全指南获取与响应式订阅 Viem Client 实例【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi导读useClient是 wagmi React 包中用于获取 ViemClient为骨架结合 packages/react/src/hooks/useClient.ts 及wagmi/core的底层 Action 源码完整讲解useClient的导入方式、两种参数chainId与config、返回类型、类型推导规则与响应式更新机制帮助你理解它在 wagmi 全家桶如useReadContract、useSendTransaction背后的定位并能在自定义组件中正确、高效地使用它。什么是 useClientuseClient是一个 React Hook用于获取 ViemClient实例。Viem Client 是发起链上交互读合约、发交易、订阅事件等的入口对象它包含链信息、账户、传输方式transport等核心配置。在 wagmi 中Config通过createConfig创建并为每一条已配置的链维护一个对应的 Viem Client见 packages/core/src/createConfig.ts 中的clientsMap 与getClient方法。useClient将这一过程接入 React 的响应式体系默认从最近的WagmiProvider中读取Config通过useSyncExternalStoreWithSelector订阅配置的变更如切换链当链或 Client 变化时自动触发组件重渲染返回当前链对应的Client | undefined。文档中对它的定位是“Hook for getting ViemClientinstance.”即专为在 React 组件中获取 Viem Client 而设计。Import 导入方式与 wagmi 其它 React Hook 一致直接从wagmi包导入import { useClient } from wagmi对应的类型定义同样从wagmi导出在本文后续“参数”与“返回类型”小节中会分别用到import { type UseClientParameters } from wagmi import { type UseClientReturnType } from wagmiUsage 基础用法最基础的用法是不传任何参数直接从默认配置中取出当前链的 Clientimport { useClient } from wagmi function App() { const client useClient() }其中config通过createConfig创建例如使用 site/snippets/react/config.ts 中的示例配置配置了mainnet与sepolia两条链均使用http()传输import { createConfig, http } from wagmi import { mainnet, sepolia } from wagmi/chains export const config createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })当应用处于已连接状态时client会指向当前活动链即config.state.chainId对应的链的 Viem Client未连接或未配置链时返回undefined。基础用法背后的行为验证仓库测试 packages/react/src/hooks/useClient.test.ts 验证了默认行为test(default, async () { const { result, rerender } await renderHook(() useClient()) expect(result.current?.chain.id).toEqual(1) // mainnet await switchChain(config, { chainId: 456 }) rerender() expect(result.current?.chain.id).toEqual(456) })可以看到默认情况下useClient()返回 mainnetid 为 1的 Client调用switchChain切换链并重渲染后useClient自动返回新链456对应的 Client。这正是它“响应式”的体现——链切换后 Hook 返回值随之更新。Parameters 参数详解UseClientParameters本质上是wagmi/core中GetClientParameters与ConfigParameter的交叉类型见 packages/react/src/hooks/useClient.tsexport type UseClientParameters config extends Config Config, chainId extends config[chains][number][id] | number | undefined ..., ComputeGetClientParametersconfig, chainId ConfigParameterconfig它包含两个可选字段chainIdconfig[chains][number][id] | undefined要获取 Client 的目标链 ID。不传时使用当前活动链config.state.chainId。import { useClient } from wagmi import { mainnet } from wagmi/chains import { config } from ./config function App() { const client useClient({ chainId: mainnet.id, }) }对应的wagmi/core配置示例见 site/snippets/core/config.ts。类型层面的约束当传入明确配置过的chainId时返回类型会被收窄为对应链的精确 Client 类型。类型测试 packages/react/src/hooks/useClient.test-d.ts 对此有专门验证test(parameters: chainId, () { const client useClient({ config, chainId: chain.mainnet.id, }) expectTypeOf(client.chain).toEqualTypeOftypeof chain.mainnet() expectTypeOf(client.chain).not.toEqualTypeOftypeof chain.mainnet2() })也就是说指定chainId: mainnet.id后client.chain会被精确推断为mainnet链类型而非泛化的Chain。运行时行为若传入的chainId未在createConfig中配置则返回undefined。测试验证了这一点test(behavior: unconfigured chain, async () { const { result } await renderHook(() useClient({ chainId: 123456 })) expect(result.current).toBeUndefined() })底层原因可追溯至 packages/core/src/actions/getClient.ts 的容错处理getClient内部调用config.getClient(parameters)当目标链未配置时会抛出ChainNotConfiguredError而getClientAction 通过try/catch将其吞掉并返回undefinedexport function getClientconfig extends Config, chainId extends ...( config: config, parameters: GetClientParametersconfig, chainId {}, ): GetClientReturnTypeconfig, chainId { try { return config.getClient(parameters) as GetClientReturnTypeconfig, chainId } catch { return undefined as never } }而config.getClient的实现见 packages/core/src/createConfig.ts会先校验chainId是否在已配置链中未配置则抛出ChainNotConfiguredError如果存在已缓存的 Client 则直接复用clientsMap 按链 ID 记忆化否则按当前链构建新的 Client 并缓存。configConfig | undefined显式指定要使用的Config代替从最近的WagmiProvider中读取。这在需要绕过 Provider 上下文、或在测试与工具函数中直接注入配置时非常有用import { useClient } from wagmi import { config } from ./config function App() { const client useClient({ config, }) }从源码看useClient内部通过useConfig(parameters)解析配置传入parameters.config时优先使用显式配置否则回退到上下文中的WagmiProvider配置见 packages/react/src/hooks/useClient.ts。测试 packages/react/src/hooks/useClient.test.ts 也验证了显式传入config的用法test(parameters: config, async () { const { result } await renderHook(() useClient({ config }), { wrapper: ({ children }) createElement(Fragment, { children }), }) expect(result.current).toBeDefined() })注意这里 wrapper 使用了空的Fragment没有WagmiProvider仅凭显式传入的config即可正常工作——这正是config参数的典型应用场景。Return Type 返回类型Client | undefinedUseClientReturnType直接透传wagmi/core的GetClientReturnType见 packages/react/src/hooks/useClient.ts其类型逻辑定义在 packages/core/src/actions/getClient.ts若解析出的chainId是已配置链的 ID返回该链对应的、携带精确传输与链类型的Client否则返回undefined。因此在实际使用时需要处理undefined分支例如function App() { const client useClient() if (!client) return null // client 在此处被类型收窄为确定的 Viem Client }类型测试对未配置链分支也有验证test(behavior: unconfigured chain, () { const client useClient({ chainId: 123456 }) if (client) { expectTypeOf(client.chain).toEqualTypeOfChain() expectTypeOf(client.transport.type).toEqualTypeOfstring() } else { expectTypeOf(client).toEqualTypeOfundefined() } })而如果显式传入config且chainId不在配置链中TypeScript 会直接报类型错误ts-expect-error标记因为类型层面已经知道该链不存在const client useClient({ config, // ts-expect-error chainId: 123456, }) expectTypeOf(client).toEqualTypeOfundefined()响应式机制useClient 如何随链切换自动更新useClient的核心实现非常精简全部依赖useSyncExternalStoreWithSelector将wagmi/core的命令式 API 桥接为 React 的响应式状态见 packages/react/src/hooks/useClient.tsreturn useSyncExternalStoreWithSelector( (onChange) watchClient(config, { onChange }), () getClient(config, parameters), () getClient(config, parameters), (x) x, (a, b) a?.uid b?.uid, ) as any各部分职责如下订阅源subscribe通过watchClient(config, { onChange })订阅配置变更。watchClient是wagmi/core的 Action见 packages/core/src/actions/watchClient.ts它内部调用config.subscribe并把getClient(config)的结果作为快照传给onChange同时返回一个取消订阅函数供 React 在组件卸载时清理。快照读取getSnapshot分别作为服务端/首次渲染与客户端快照直接调用getClient(config, parameters)获取当前 Client两次调用保持一致的快照语义避免 hydration 不一致。选择器与相等性判断(x) x选择器直接返回整个 Client相等性函数(a, b) a?.uid b?.uid以 Viem Client 的uid为比较依据只有当 Client 实例真的发生变化而非每次渲染生成新对象时才触发重渲染从而避免无谓的组件更新。这条调用链可以完整概括为useClient (React Hook) └─ useConfig 解析 Config显式参数 or WagmiProvider 上下文 └─ watchClient wagmi/core Actionconfig.subscribe 订阅变更 └─ getClient wagmi/core Actionconfig.getClient 读取快照未配置链返回 undefined └─ createConfig 内部实现clients Map 记忆化缓存 / ChainNotConfiguredError这也解释了为什么switchChain之后调用rerender()即可拿到新链的 Client链切换会更新config的 store 状态watchClient的订阅回调被触发useSyncExternalStoreWithSelector检测到uid变化后让组件重新渲染从而拿到最新的Client。useClient 与相关 Action / Hook 的关系在 wagmi 中useClient与wagmi/core的两个 Action 一一对应文档末尾也给出了关联入口层次命令式 API响应式 Hook读取 ClientgetClientwagmi/coreuseClient订阅 Client 变更watchClientwagmi/coreuseClient内部自动订阅getClient的用法见 site/core/api/actions/getClient.mdimport { getClient } from wagmi/core import { config } from ./config const client getClient(config)import { getClient } from wagmi/core import { mainnet } from wagmi/core/chains import { config } from ./config const client await getClient(config, { chainId: mainnet.id, })两者区别在于在 React 组件中应使用useClient以获得自动的响应式更新与生命周期管理而在组件之外如工具函数、事件处理器需要手动维护订阅则应使用getClient/watchClient组合。此外useClient也是许多其他 wagmi Hook 的基石——例如useReadContract、useSendTransaction等内部都依赖 Client 来执行具体的 Viem 调用。理解了useClient也就理解了 wagmi 如何在 React 世界中管理链上交互的核心入口。总结useClient从wagmi导入返回当前链或指定chainId链的 ViemClient | undefined。两个参数chainId目标链 ID未配置时返回undefined与config显式覆盖WagmiProvider上下文配置。底层由getClientwatchClient两个wagmi/coreAction 支撑通过useSyncExternalStoreWithSelector接入 React以 Clientuid判断变化实现链切换后的自动更新。源码入口packages/react/src/hooks/useClient.ts配套测试见 packages/react/src/hooks/useClient.test.ts 与 packages/react/src/hooks/useClient.test-d.ts。【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考