wagmi Solid 中的 useConnectorClient:如何从活跃 Connector 获取 Viem Wallet Client
2026/9/17 11:29:05 网站建设 项目流程

wagmi Solid 中的 useConnectorClient:如何从活跃 Connector 获取 Viem Wallet Client

【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

本文围绕 wagmi 的 Solid 适配器原语useConnectorClient(定义于 packages/solid/src/primitives/useConnectorClient.ts)展开,讲解它如何在 SolidJS 应用中以响应式方式获取活跃 Connector 对应的 Viem Wallet Client:包括参数(account/chainId/config/connector)的含义与默认行为、查询选项、返回结构,以及缓存失效(断连时清除、换地址时失效)等生命周期机制。读完你应能在 Solid 项目中安全地拿到可调用writeContractwrite等方法的 Wallet Client,并理解其背后的getConnectorClientaction 与 TanStack Query 集成细节。

用途与导入

useConnectorClient是一个用于从当前活跃 Connector获取 [Viem Wallet Client] 的原语(primitive)。典型场景是:用户已连接钱包(如 MetaMask、Coinbase Wallet),你需要一个绑定该钱包账号与链的 Client 来发送交易或调用合约写方法。

导入方式:

import { useConnectorClient } from '@wagmi/solid'

基本用法

import { useConnectorClient } from '@wagmi/solid' function App() { const client = useConnectorClient() // client() 在已连接时返回 Wallet Client }

配套的配置示例(摘自 site/snippets/solid/config.ts):

import { createConfig, http } from '@wagmi/solid' import { mainnet, sepolia } from '@wagmi/solid/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })

注意一个与 React 版本的显著差异:Solid 中参数必须作为 getter 函数(Accessor)传入,以维持响应性(详见 site/solid/api/primitives.md):

useConnectorClient(() => ({ chainId: 1, connector: myConnector, }))

从源码看(useConnectorClient.ts),参数的类型正是Accessor<SolidParameters<...>>,默认值为() => ({}),因此不传参数时取的是最近WagmiProvider中的 config 与当前连接状态。

参数详解

account

Address | undefined

指定 Wallet Client 使用的账号。底层 action 的完整签名(见 getConnectorClient.ts)实际接受Address | Account | null | undefined

  • 传入地址/账号时,该账号必须存在于 Connector 上,否则会抛出ConnectorAccountNotFoundError
  • 传入null表示账号可能不存在,适用于可从 Connector 推断账号的场景(如未连接时发起调用,由钱包内弹窗选择账号);
  • 不传时默认使用连接中的第一个账号(connection.accounts[0])。

chainId

config['chains'][number]['id'] | undefined

Client 要使用的链 ID。不传时默认取当前连接的链:Solid 实现中通过useChainId原语响应式获取(useConnectorClient.ts):

const config = useConfig(parameters) const chainId = useChainId(() => ({ config: config() })) const connection = useConnection(() => ({ config: config() })) const options = createMemo(() => getConnectorClientQueryOptions<config, chainId>(config(), { ...(parameters() as any), chainId: parameters().chainId ?? chainId(), connector: parameters().connector ?? connection().connector, }), )

也就是说,chainIdconnector都支持"显式传入 > 当前连接状态"的回退链,且由于包在createMemo中,切换链(useSwitchChain)会触发查询选项的重新计算。类型测试(useConnectorClient.test-d.ts)验证了这一推导:指定configclient.data?.chain?.id被推导为1 | 456 | 10,再显式传chainId: 1则收窄为1

config

Config | undefined

覆盖从最近WagmiProvider获取的Config。用于多 Provider 嵌套或测试场景中注入自定义 config。

connector

Connector | undefined

指定要获取 Client 的 Connector,默认是活跃 Connector(即config.state.current对应的连接)。

connector还决定了查询是否启用。核心查询选项构建函数getConnectorClientQueryOptions(packages/core/src/query/getConnectorClient.ts)中有这样的逻辑:

enabled: Boolean(options.connector?.getProvider && (options.query?.enabled ?? true)),

即仅当 connector 实现了getProvider时查询才默认启用;同时该函数强制了gcTime: 0staleTime: Number.POSITIVE_INFINITY(Client 一旦被获取便视为长时有效,缓存交由原语自行清理)。

query(TanStack Query 选项)

query参数接受一组 TanStack Query 选项,但 wagmi 并不支持全部参数——queryFnqueryKey等由内部使用、不可覆盖。支持的常用项包括:enabled(设为false可禁用自动查询,实现依赖查询)、initialData/initialDataUpdatedAtmetanetworkModenotifyOnChangePropsplaceholderDataqueryClientrefetchIntervalrefetchIntervalInBackgroundrefetchOnMountrefetchOnReconnectrefetchOnWindowFocusretryretryDelayretryOnMountselectstructuralSharing(完整说明见 site/shared/query-options.md)。注意gcTimestaleTime在本原语中由内部固定,不在可传之列(对应类型中Omit<..., 'gcTime' | 'staleTime'>)。

返回类型

useConnectorClient.ReturnType // = UseQueryReturnType<GetConnectorClientData<config, chainId>, GetConnectorClientErrorType>

它本质上是一个createQuery的结果(经 packages/solid/src/utils/query.ts 中的useQuery封装),因此包含 TanStack Query 的标准状态与访问器:

  • 状态:statuspending/success/error)、fetchStatusisPendingisLoadingisSuccessisErrorisFetchingisStale等;
  • 数据:data(成功时为 Viem Client,含accountchain)、dataUpdatedAt
  • 错误:error(类型为GetConnectorClientErrorType)、errorUpdateCountfailureCount等;
  • 操作:refetchqueryKey

queryKey的结构为['connectorClient', { chainId, ... }](由getConnectorClientQueryKey生成,见 getConnectorClient.ts),默认状态下测试快照可直观看到:

queryKey: ["connectorClient", { chainId: 1 }]

可能的错误类型(GetConnectorClientErrorType)包括:ConnectorAccountNotFoundErrorConnectorChainMismatchErrorConnectorNotConnectedErrorConnectorUnavailableReconnectingError以及 Viem 的BaseError

生命周期:缓存清理与失效

useConnectorClient除了查询本身,还通过createEffect管理了连接状态变化时的缓存(useConnectorClient.ts):

const queryClient = useQueryClient() createEffect( on( () => connection().address, (currentAddress, previousAddress) => { if (!currentAddress && previousAddress) { // 账号断开时移除缓存 queryClient.removeQueries({ queryKey: options().queryKey }) } else if (currentAddress !== previousAddress) { // 地址变化时使缓存失效 queryClient.invalidateQueries({ queryKey: options().queryKey }) } }, { defer: true }, ), )

这意味着:断连后旧 Client 缓存会被移除(避免残留指向已失效账号的 Client),地址切换时旧缓存被标记失效并重新获取。测试用例(useConnectorClient.test.ts)对这套行为做了完整验证:

  • behavior: connect and disconnect:连接前dataundefineduseConnect().mutate(...)data就绪,useDisconnect().mutate()data重新变为undefined
  • behavior: switch chainsuseSwitchChain切换到链 456 后,data.chain.id变为456,切回 1 后恢复;
  • behavior: disabled when connecting:config 状态为connecting时查询不进入 loading(等待连接完成)。

底层 action:getConnectorClient

原语底层复用了 core 包的getConnectorClientaction(packages/core/src/actions/getConnectorClient.ts),其执行链路值得了解:

  1. 定位连接:显式传connector时,并行调用connector.getAccounts()connector.getChainId()构造连接;否则读取config.state.connections.get(config.state.current)。无连接则抛ConnectorNotConnectedError。若 config 处于reconnecting且 connector 无法离线读取账号/链,则抛ConnectorUnavailableReconnectingError
  2. 链一致性断言:默认assertChainId = true,当connection.connector.getChainId()与目标chainId不一致时抛ConnectorChainMismatchError,防止 Client 指向与钱包实际链不符的环境;
  3. 优先使用 connector 自定义 Client:若 connector 实现了getClient,直接返回其结果(见 getConnectorClient.ts);
  4. 默认路径:取账号(默认connection.accounts[0],并做地址校验和),通过connector.getProvider({ chainId })获取 Provider,然后用 Viem 的createClientcustomtransport(retryCount: 0,即不做 RPC 重试)创建 Client:
return createClient({ account, chain, name: 'Connector Client', transport: (opts) => custom(provider)({ ...opts, retryCount: 0 }), })

这也解释了为何返回的 Client 是"直连钱包 Provider"的写能力载体,而不是走 config 中配置的httptransport。

与其他原语的组合

useConnectorClient是写操作链路的一环,可自然与仓库中的其他 Solid 原语组合:

  • useConnect / useDisconnect:驱动连接状态,useConnectorClientdata随之出现/消失;
  • useSwitchChain:切链后 Client 的chain自动更新(见上文测试);
  • useWriteContract:拿到 Client 后发送交易。

小结

  • useConnectorClient是 Solid 端获取活跃 Connector 对应 Viem Wallet Client 的响应式原语,参数需以 getter 函数传入;
  • 四个核心参数account/chainId/config/connector均有合理的回退默认值(连接账号、连接链、Provider 的 config、活跃 connector),不传参即可在大多数场景直接使用;
  • 查询在 connector 未就绪(如connecting状态、无getProvider)时保持pending;底层固定staleTime: InfinitygcTime: 0,由原语在断连时removeQueries、换地址时invalidateQueries管理缓存;
  • 失败时会以ConnectorNotConnectedErrorConnectorChainMismatchErrorConnectorAccountNotFoundError等类型化错误暴露问题,便于在error分支中精确处理。

【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询