wagmi 核心 ActionwatchConnectors完全指南:订阅连接器变化、事件回调与清理机制
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
watchConnectors是 wagmi 核心包(@wagmi/core)提供的一个响应式监听 Action,用于订阅 Config 中连接器(Connector)集合的变化,并在变化发生时触发回调。在钱包插件动态注入、EIP-6963 多钱包提供商动态发现等场景下,连接器列表会随时间变化,本指南将带你掌握watchConnectors的导入方式、完整用法、参数签名、返回值语义,并结合 核心实现源码 与 单元测试 深入其底层订阅机制,使你能够正确地监听、响应与清理连接器变化事件。
为什么需要监听 Connectors 的变化
在 wagmi 中,Connector 代表一个可用的钱包连接器(如 MetaMask、Coinbase Wallet、WalletConnect 等),它们被集中管理在 Config 内部。连接器集合并不是一成不变的:
- 基于EIP-6963的多钱包提供商发现(Multi Injected Provider Discovery,MIPD)机制下,浏览器中已安装的钱包扩展会动态注入/移除 Provider,连接器列表会随之增减;
- 应用运行时可能通过编程方式(如调用
config._internal.connectors.setState或重新setup连接器)向集合中添加新连接器。
因此,当需要根据"当前可用钱包列表"动态渲染 UI(例如钱包选择弹窗)时,就必须订阅连接器变化——这正是watchConnectors的职责。它从 createConfig.ts 中创建的 connectors 内部 store 订阅变化,并在变化时把最新连接器数组和上一次的连接器数组一起交给回调函数。
Import:从@wagmi/core导入
watchConnectors是@wagmi/core的顶层导出之一,可以直接从主入口导入:
import { watchConnectors } from '@wagmi/core'它同样被收录在核心包的 actions 导出中(见 exports/actions.ts 与 exports/index.ts),因此无论是import { watchConnectors } from '@wagmi/core'还是从 actions 子路径导入均可使用。
基础用法:订阅、回调与清理
最小可运行示例
// index.ts import { watchConnectors } from '@wagmi/core' import { config } from './config' const unwatch = watchConnectors(config, { onChange(connectors) { console.log('Connectors changed!', connectors) }, }) // 不再需要监听时,调用返回的清理函数 unwatch()其中config是使用createConfig创建的 Config 实例,完整的创建示例可参考 site/snippets/core/config.ts:
// config.ts import { http, createConfig } from '@wagmi/core' import { mainnet, sepolia } from '@wagmi/core/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })典型应用场景:动态钱包列表 UI
在实际 DApp 中,最常见的使用方式是结合状态管理维护一份"可用钱包"列表:
import { watchConnectors } from '@wagmi/core' import { config } from './config' let availableConnectors: ReturnType<typeof config.connectors> = config.connectors const unwatch = watchConnectors(config, { onChange(connectors, prevConnectors) { // 更新 UI 状态:钱包列表发生了变化 availableConnectors = connectors console.log('Connectors changed!', connectors) console.log('Previous connectors:', prevConnectors) }, }) // 组件卸载或页面销毁时清理 unwatch()注意:
watchConnectors仅会在连接器集合发生变化时触发onChange。如果你需要监听的是"当前连接了哪个连接器/哪个账户"(即 connection 的变化),应使用watchConnection/watchConnections(对应实现见 watchConnection.ts 与 watchConnections.ts)。
参数(Parameters)
watchConnectors接受两个参数:
config:由createConfig创建的 Config 实例;parameters:类型为WatchConnectorsParameters,包含唯一的onChange回调。
onChange
类型签名:
onChange(connectors: GetConnectorsReturnType, prevConnectors: GetConnectorsReturnType): voidconnectors:变化后的连接器数组,其类型GetConnectorsReturnType与 getConnectors.ts 的返回值一致(即config['connectors'],元素类型为Connector);prevConnectors:变化前的连接器数组;- 返回值
void,用于在连接器变化时执行副作用(如刷新 UI、记录日志、重新计算可用钱包等)。
import { watchConnectors } from '@wagmi/core' import { config } from './config' const unwatch = watchConnectors(config, { onChange(connectors) { // [!code focus:3] console.log('Connectors changed!', connectors) }, }) unwatch()从源码(watchConnectors.ts)可以确认,WatchConnectorsParameters中目前只有onChange这一个必填字段,并且onChange的两个参数都直接透传自底层 store 的订阅回调。类型定义还支持泛型config extends Config,配合 wagmi 的模块增强(Register)可以获得完全类型化的连接器数组。
返回值(Return Type)
import { type WatchConnectorsReturnType } from '@wagmi/core'watchConnectors返回一个清理函数(() => void),类型为WatchConnectorsReturnType。调用它即可停止订阅、解除监听,避免内存泄漏:
const unwatch = watchConnectors(config, { onChange(connectors) { console.log('Connectors changed!', connectors) }, }) // 清理 unwatch()源码中WatchConnectorsReturnType被定义为() => void(见 watchConnectors.ts),它直接透传自内部 store 的subscribe返回值,因此与底层订阅生命周期严格一一对应。
底层实现原理
核心调用链
watchConnectors的实现非常精简(完整源码见 watchConnectors.ts):
export function watchConnectors<config extends Config>( config: config, parameters: WatchConnectorsParameters<config>, ): WatchConnectorsReturnType { const { onChange } = parameters return config._internal.connectors.subscribe((connectors, prevConnectors) => { onChange(Object.values(connectors), prevConnectors) }) }可以看到,watchConnectors本质上是对 Config 内部 connectors store 的subscribe的薄封装:
- 从参数中解构出
onChange; - 调用
config._internal.connectors.subscribe(listener)注册监听; - store 在状态变化时以
(connectors, prevConnectors)形式回调; - 由于 store 内部以对象形式保存连接器集合,源码用
Object.values(connectors)将其转换为数组后再交给onChange; - 直接把
subscribe返回的取消订阅函数作为watchConnectors的返回值。
connectors store 从何而来
这个内部 store 在 createConfig.ts 中创建:
const connectors = createStore(() => { const collection = [] const rdnsSet = new Set<string>() for (const connectorFns of rest.connectors ?? []) { const connector = setup(connectorFns) collection.push(connector) // ...收集 rdns 用于 EIP-6963 去重 } if (!ssr && mipd) { const providers = mipd.getProviders() for (const provider of providers) { if (rdnsSet.has(provider.info.rdns)) continue collection.push(setup(providerDetailToConnector(provider))) } } return collection })它综合了两类来源:一是用户在createConfig中显式传入的connectors,二是通过 MIPD(EIP-6963)自动发现并setup的浏览器钱包提供商。每个连接器在setup阶段会被绑定独立的 emitter 与uid(见 createConfig.ts),从而保证集合内每个连接器可被唯一标识。
变化从何触发
连接器集合的更新主要来自两条路径:
- MIPD 动态发现:Config 在创建时会对 MIPD 的 provider 变化进行订阅(见 createConfig.ts),当浏览器中新注入或移除钱包扩展时,会基于
rdns去重后把新连接器合并进集合,从而触发watchConnectors的回调; - 编程式更新:应用代码可以直接操作内部 store,例如
config._internal.connectors.setState(...)来增删连接器。
测试如何验证订阅行为
在 watchConnectors.test.ts 中,测试用例完整演示了"订阅 → 触发变化 → 收到回调 → 清理"的完整闭环:
test('default', async () => { const connectors: (readonly Connector[])[] = [] const unwatch = watchConnectors(config, { onChange(connector) { connectors.push(connector) }, }) const count = config.connectors.length // 向内部 store 追加一个新的 mock 连接器,模拟运行时连接器集合变化 config._internal.connectors.setState(() => [ ...config.connectors, config._internal.connectors.setup(mock({ accounts })), ]) expect(config.connectors.length).toBe(count + 1) unwatch() })该测试同时印证了两个关键行为:watchConnectors的回调会在连接器集合被修改(setState)后触发,并且返回的unwatch函数可以正常结束订阅。
与 React/Vue/Solid 响应式 Hooks 的关系
watchConnectors是框架无关的核心层 API,各框架适配层均在其之上封装了响应式 Hook/Composable:
- React:
useConnectorsHook 通过useSyncExternalStore桥接,订阅端调用watchConnectors(config, { onChange }),读取端调用getConnectors(config)(完整实现见 useConnectors.ts),使连接器数组的每次变化都能自动触发组件重渲染,且服务端渲染(SSR)时返回一致的快照; - 需要说明的是,官方仓库中的 Vue 与 Solid 适配层同样以
@wagmi/core的 watch Action 为基础构建各自的响应式封装。
因此,在 React 应用中通常优先使用useConnectors;而在非框架环境、自定义渲染逻辑或需要精细控制订阅生命周期的场景下,直接使用watchConnectors是最合适的选择。
注意事项与最佳实践
- 务必调用清理函数:
watchConnectors返回的unwatch应在其对应作用域销毁时调用(组件卸载、页面关闭、单例服务停用等),否则订阅将持续存在并可能造成内存泄漏或重复回调。 - 回调触发条件:只有连接器集合发生变化才会触发
onChange;若需要响应连接状态(如连接/断开钱包),请改用watchConnection/watchConnections。 - 参数顺序:
onChange的第一个参数是"最新连接器数组",第二个参数是"上一次的连接器数组",可用于对比差异、增量渲染。 - 类型安全:
WatchConnectorsParameters与WatchConnectorsReturnType均从@wagmi/core导出,配合泛型与Register模块增强可得到与 Config 完全一致的连接器类型推导。 - SSR 环境:在服务端渲染时,MIPD 自动发现被跳过(见 createConfig.ts),连接器集合以显式配置为准,因此
watchConnectors的回调在服务端通常不会被触发,这属于预期行为。
小结
watchConnectors是 wagmi 中监听连接器集合变化的官方入口:导入自@wagmi/core,传入config与onChange回调即可订阅,返回的unwatch函数用于清理。其底层是对 Config 内部 connectors store 的subscribe的封装(源码见 watchConnectors.ts),配合 EIP-6963 多钱包动态发现机制(见 createConfig.ts),让应用能够实时感知"可用钱包列表"的变化,是构建动态钱包选择器、钱包发现类功能的基石能力。
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考