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 项目中安全地拿到可调用writeContract、write等方法的 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, }), )也就是说,chainId与connector都支持"显式传入 > 当前连接状态"的回退链,且由于包在createMemo中,切换链(useSwitchChain)会触发查询选项的重新计算。类型测试(useConnectorClient.test-d.ts)验证了这一推导:指定config后client.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: 0与staleTime: Number.POSITIVE_INFINITY(Client 一旦被获取便视为长时有效,缓存交由原语自行清理)。
query(TanStack Query 选项)
query参数接受一组 TanStack Query 选项,但 wagmi 并不支持全部参数——queryFn、queryKey等由内部使用、不可覆盖。支持的常用项包括:enabled(设为false可禁用自动查询,实现依赖查询)、initialData/initialDataUpdatedAt、meta、networkMode、notifyOnChangeProps、placeholderData、queryClient、refetchInterval、refetchIntervalInBackground、refetchOnMount、refetchOnReconnect、refetchOnWindowFocus、retry、retryDelay、retryOnMount、select、structuralSharing(完整说明见 site/shared/query-options.md)。注意gcTime与staleTime在本原语中由内部固定,不在可传之列(对应类型中Omit<..., 'gcTime' | 'staleTime'>)。
返回类型
useConnectorClient.ReturnType // = UseQueryReturnType<GetConnectorClientData<config, chainId>, GetConnectorClientErrorType>它本质上是一个createQuery的结果(经 packages/solid/src/utils/query.ts 中的useQuery封装),因此包含 TanStack Query 的标准状态与访问器:
- 状态:
status(pending/success/error)、fetchStatus、isPending、isLoading、isSuccess、isError、isFetching、isStale等; - 数据:
data(成功时为 Viem Client,含account与chain)、dataUpdatedAt; - 错误:
error(类型为GetConnectorClientErrorType)、errorUpdateCount、failureCount等; - 操作:
refetch、queryKey。
queryKey的结构为['connectorClient', { chainId, ... }](由getConnectorClientQueryKey生成,见 getConnectorClient.ts),默认状态下测试快照可直观看到:
queryKey: ["connectorClient", { chainId: 1 }]可能的错误类型(GetConnectorClientErrorType)包括:ConnectorAccountNotFoundError、ConnectorChainMismatchError、ConnectorNotConnectedError、ConnectorUnavailableReconnectingError以及 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:连接前data为undefined,useConnect().mutate(...)后data就绪,useDisconnect().mutate()后data重新变为undefined;behavior: switch chains:useSwitchChain切换到链 456 后,data.chain.id变为456,切回 1 后恢复;behavior: disabled when connecting:config 状态为connecting时查询不进入 loading(等待连接完成)。
底层 action:getConnectorClient
原语底层复用了 core 包的getConnectorClientaction(packages/core/src/actions/getConnectorClient.ts),其执行链路值得了解:
- 定位连接:显式传
connector时,并行调用connector.getAccounts()与connector.getChainId()构造连接;否则读取config.state.connections.get(config.state.current)。无连接则抛ConnectorNotConnectedError。若 config 处于reconnecting且 connector 无法离线读取账号/链,则抛ConnectorUnavailableReconnectingError; - 链一致性断言:默认
assertChainId = true,当connection.connector.getChainId()与目标chainId不一致时抛ConnectorChainMismatchError,防止 Client 指向与钱包实际链不符的环境; - 优先使用 connector 自定义 Client:若 connector 实现了
getClient,直接返回其结果(见 getConnectorClient.ts); - 默认路径:取账号(默认
connection.accounts[0],并做地址校验和),通过connector.getProvider({ chainId })获取 Provider,然后用 Viem 的createClient以customtransport(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:驱动连接状态,
useConnectorClient的data随之出现/消失; - useSwitchChain:切链后 Client 的
chain自动更新(见上文测试); - useWriteContract:拿到 Client 后发送交易。
小结
useConnectorClient是 Solid 端获取活跃 Connector 对应 Viem Wallet Client 的响应式原语,参数需以 getter 函数传入;- 四个核心参数
account/chainId/config/connector均有合理的回退默认值(连接账号、连接链、Provider 的 config、活跃 connector),不传参即可在大多数场景直接使用; - 查询在 connector 未就绪(如
connecting状态、无getProvider)时保持pending;底层固定staleTime: Infinity、gcTime: 0,由原语在断连时removeQueries、换地址时invalidateQueries管理缓存; - 失败时会以
ConnectorNotConnectedError、ConnectorChainMismatchError、ConnectorAccountNotFoundError等类型化错误暴露问题,便于在error分支中精确处理。
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考