news 2026/9/17 3:01:26

useEnsAvatar:在 wagmi 中获取 ENS 头像的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
useEnsAvatar:在 wagmi 中获取 ENS 头像的完整指南

useEnsAvatar:在 wagmi 中获取 ENS 头像的完整指南

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

useEnsAvatar是 wagmi 提供的 React Hook,用于根据 ENS 名称(ENS name)获取对应的头像(avatar)URL。本文以 wagmi 仓库中的官方文档 site/react/api/hooks/useEnsAvatar.md 为主体,结合 hooks 源码、core action 实现 与 测试用例 展开讲解,覆盖导入方式、基本用法、全部参数含义、查询选项、返回类型,以及底层查询键与缓存机制,帮助你快速在 React 应用中实现 ENS 头像展示。

导入 Hook

在 React 组件中,通过以下方式从wagmi导入useEnsAvatar

import { useEnsAvatar } from 'wagmi'

useEnsAvatar基于 TanStack Query 实现,返回的是一个查询结果对象(而非直接的字符串),因此可以自然融入WagmiProvider+createConfig的响应式数据流中。

基本用法

下面是一个最小示例,通过viem/ens提供的normalize函数将 ENS 名称规范化后传入 Hook:

import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ name: normalize('wevm.eth'), }) }

配套的配置如下(示例配置片段来自 site/snippets/react/config.ts):

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(), }, })

为什么必须先normalizeENS 名称禁止某些特殊字符(如下划线_),且存在其他校验规则,因此在传给useEnsAvatar之前,建议先使用 UTS-46 规范化 处理名称。Viem 内置的normalize函数正是为此设计,可避免因名称未规范化导致的解析失败。

参数(Parameters)

useEnsAvatar接受一个UseEnsAvatarParameters类型的参数对象,类型定义如下:

import { type UseEnsAvatarParameters } from 'wagmi'

从源码看,该类型是GetEnsAvatarOptions<config, selectData> & ConfigParameter<config>的合并结果(见 packages/react/src/hooks/useEnsAvatar.ts#L17-L20),即同时包含查询参数与 wagmi 配置参数。

assetGatewayUrls

  • 类型:{ ipfs?: string | undefined; arweave?: string | undefined } | undefined
  • 说明:用于解析 IPFS 和/或 Arweave 资产的自定义网关 URL。当头像指向 IPFS 或 Arweave 上的资源时,可通过该参数指定可访问的网关。
  • 版本要求:viem@>=2.3.1
import { getEnsAvatar } from '@wagmi/core' import { normalize } from 'viem/ens' import { config } from './config' function App() { const result = useEnsAvatar({ assetGatewayUrls: { ipfs: 'https://cloudflare-ipfs.com', }, name: normalize('wevm.eth'), }) }

blockNumber

  • 类型:bigint | undefined
  • 说明:指定在某个区块高度(block number)上获取 ENS 头像,用于查询历史状态的头像。
import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ blockNumber: 17829139n, name: normalize('wevm.eth'), }) }

blockTag

  • 类型:'latest' | 'earliest' | 'pending' | 'safe' | 'finalized' | undefined
  • 说明:指定获取头像时使用的区块标签(block tag),默认通常为'latest'
import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ name: normalize('wevm.eth'), blockTag: 'latest', }) }

chainId

  • 类型:config['chains'][number]['id'] | undefined
  • 说明:指定执行查询的链 ID。未指定时,Hook 会通过useChainId自动使用当前激活的链。
import { useEnsAvatar } from 'wagmi' import { mainnet } from 'wagmi/chains' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ chainId: mainnet.id, name: normalize('wevm.eth'), }) }

config

  • 类型:Config | undefined
  • 说明:显式传入Config以替代从最近的WagmiProvider中读取的配置。适用于脱离 Provider 上下文或需要覆盖配置的场景。
import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' import { config } from './config' function App() { const result = useEnsAvatar({ config, name: normalize('wevm.eth'), }) }

gatewayUrls

  • 类型:string[] | undefined
  • 说明:一组 Universal Resolver 网关地址,用于解析通过 ENS Universal Resolver 合约发起的 CCIP-Read 请求。
import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ gatewayUrls: ['https://cloudflare-ipfs.com'], name: normalize('wevm.eth'), }) }

name

  • 类型:string | undefined
  • 说明:要获取头像的 ENS 名称。nameundefined时,enabled会被自动置为false,查询不会执行(该逻辑见下文源码剖析部分)。
import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ name: normalize('wevm.eth'), }) }

scopeKey

  • 类型:string | undefined
  • 说明:将缓存限定在给定上下文范围内。具有相同scopeKey的 Hook 会共享同一份缓存,适用于需要在不同场景下隔离或复用缓存数据的情形。
import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ name: normalize('wevm.eth'), scopeKey: 'foo', }) }

universalResolverAddress

  • 类型:Address | undefined
  • 说明:
    • ENS Universal Resolver 合约地址;
    • 未指定时,默认使用当前链的 Universal Resolver 合约地址。
import { useEnsAvatar } from 'wagmi' import { normalize } from 'viem/ens' function App() { const result = useEnsAvatar({ name: normalize('wevm.eth'), universalResolverAddress: '0x74E20Bd2A1fE0cdbe45b9A1d89cb7e0a45b36376', }) }

查询选项(query)

useEnsAvatar透传 TanStack Query 的查询选项(详见 site/shared/query-options.md)。需要特别说明:wagmi 不允许覆盖所有 TanStack Query 参数,其中queryFnqueryKey由 wagmi 内部使用,不可自定义;下表所列参数均受支持。

参数类型默认值说明
enabledboolean \| undefined设为false可禁用查询自动执行,常用于依赖查询(Dependent Queries)
gcTimenumber \| Infinity \| undefined5 * 60 * 1000(5 分钟),SSR 期间为Infinity未使用/非活跃缓存数据的保留时长,Infinity表示禁用垃圾回收
initialDataTData \| (() => TData) \| undefined作为查询初始缓存数据;函数形式仅会在共享/根查询初始化时调用一次;默认视为已过期(除非设置了staleTime);会被持久化到缓存
initialDataUpdatedAtnumber \| (() => number \| undefined) \| undefinedinitialData本身的最后更新时间(毫秒)
metaRecord<string, unknown> \| undefined附加到查询缓存条目上的额外信息,可在queryFnQueryFunctionContext中访问
networkMode'online' \| 'always' \| 'offlineFirst' \| undefined'online'网络模式
notifyOnChangePropsstring[] \| 'all' \| (() => string[] \| 'all') \| undefined默认按属性访问跟踪仅当列出的属性变化时触发组件重渲染
placeholderDataTData \| ((previousValue, previousQuery) => TData) \| undefined查询处于pending状态时使用的占位数据;不会持久化到缓存
queryClientQueryClient \| undefined最近上下文中的实例指定自定义QueryClient
refetchIntervalnumber \| false \| ((data, query) => number \| false \| undefined) \| undefined定时自动重新拉取的毫秒间隔
refetchIntervalInBackgroundboolean \| undefinedtrue时,后台标签页也会继续定时重拉取
refetchOnMountboolean \| 'always' \| ((query) => boolean \| 'always') \| undefinedtrue挂载时若数据已过期则重新拉取
refetchOnReconnectboolean \| 'always' \| ((query) => boolean \| 'always') \| undefinedtrue网络重连时若数据已过期则重新拉取
refetchOnWindowFocusboolean \| 'always' \| ((query) => boolean \| 'always') \| undefinedtrue窗口重新聚焦时若数据已过期则重新拉取
retryboolean \| number \| ((failureCount, error) => boolean) \| undefined客户端3,服务端0失败重试策略
retryDelaynumber \| ((retryAttempt, error) => number) \| undefined每次重试前的延迟毫秒数,可配合指数退避函数
retryOnMountboolean \| undefinedtrue挂载时若查询含错误是否重试
select((data: TData) => unknown) \| undefined转换/选取查询数据,只影响返回值data,不影响缓存内容
staleTimenumber \| Infinity \| undefined0数据过期时间(毫秒),Infinity表示永不过期
structuralSharingboolean \| ((oldData, newData) => TData) \| undefinedtrue是否启用查询结果间的结构共享

注意:对useEnsAvatar而言,TData = string | nullTError = GetEnsAvatarErrorType

返回类型(Return Type)

import { type UseEnsAvatarReturnType } from 'wagmi'

返回类型UseEnsAvatarReturnType实为UseQueryReturnType<string | null, GetEnsAvatarErrorType>(见 packages/react/src/hooks/useEnsAvatar.ts#L22-L23),即标准 TanStack Query 结果(详见 site/shared/query-result.md)。核心字段如下:

字段类型说明
datastring \| null最近一次成功解析的头像 URL,默认undefined
dataUpdatedAtnumber查询最近一次返回'success'状态的时间戳
errornull \| GetEnsAvatarErrorType查询抛出的错误对象,默认null
errorUpdatedAtnumber最近一次进入'error'状态的时间戳
errorUpdateCountnumber累计错误次数
failureCountnumber失败计数,成功时重置为0
failureReasonnull \| GetEnsAvatarErrorType重试失败的原因,成功时重置为null
fetchStatus'fetching' \| 'idle' \| 'paused'查询拉取状态
isError/isPending/isSuccessbooleanstatus派生的布尔标记
isFetchedboolean查询是否已完成过拉取
isFetchedAfterMountboolean组件挂载后是否拉取过(可用于隐藏旧缓存)
isFetching/isPausedbooleanfetchStatus派生的布尔标记
isLoadingboolean首次拉取进行中,等价于isFetching && isPending
isLoadingErrorboolean首次拉取是否失败
isPlaceholderDataboolean当前展示的是否为占位数据
isRefetchErrorboolean后台重拉取是否失败
isRefetchingboolean后台重拉取进行中,等价于isFetching && !isPending
isStaleboolean缓存数据是否已过期
refetch(options) => Promise<...>手动重新拉取;throwOnError控制失败时抛错还是仅记录日志,cancelRefetch控制是否取消正在运行的请求,默认true
status'error' \| 'pending' \| 'success'查询整体状态

在组件中通常会这样使用:

const { data: avatar, isPending, isError, error } = useEnsAvatar({ name: normalize('wevm.eth'), }) if (isPending) return <div>Loading avatar…</div> if (isError) return <div>Error: {error.message}</div> return avatar ? <img src={avatar} alt="ENS avatar" /> : null

TanStack Query 编程式 API

如果希望脱离 React Hook,使用底层命令式 API(例如在事件处理函数中手动触发查询),可以从wagmi/query导入对应类型与工具(详见 site/shared/query-imports.md):

import { type GetEnsAvatarData, type GetEnsAvatarOptions, type GetEnsAvatarQueryFnData, type GetEnsAvatarQueryKey, getEnsAvatarQueryKey, getEnsAvatarQueryOptions, } from 'wagmi/query'

Action:getEnsAvatar

useEnsAvatar底层委托给getEnsAvataraction。核心实现位于 packages/core/src/actions/getEnsAvatar.ts:

export function getEnsAvatar<config extends Config>( config: config, parameters: GetEnsAvatarParameters<config>, ): Promise<GetEnsAvatarReturnType> { const { chainId, ...rest } = parameters const client = config.getClient({ chainId }) const action = getAction(client, viem_getEnsAvatar, 'getEnsAvatar') return action(rest) }

调用链可概括为:useEnsAvatar(React Hook)→getEnsAvatarQueryOptions(查询选项)→getEnsAvatar(core action)→viemgetEnsAvatarviem/actions导入)→ 链上 Universal Resolver 合约解析。chainId参数被单独取出并用于从config获取对应链的 viem client,其余参数(nameblockNumberblockTaggatewayUrlsassetGatewayUrlsuniversalResolverAddress等)原样透传给 viem。

源码剖析:Hook 的底层工作原理

结合 packages/react/src/hooks/useEnsAvatar.ts 可看到完整实现:

export function useEnsAvatar< config extends Config = ResolvedRegister['config'], selectData = GetEnsAvatarData, >( parameters: UseEnsAvatarParameters<config, selectData> = {}, ): UseEnsAvatarReturnType<selectData> { const config = useConfig(parameters) const chainId = useChainId({ config }) const options = getEnsAvatarQueryOptions(config, { ...parameters, chainId: parameters.chainId ?? chainId, }) return useQuery(options) }

其工作流程为:

  1. 读取配置:通过useConfig(parameters)获取 wagmi 配置(若传入了config参数则优先使用,否则从最近的WagmiProvider上下文获取);
  2. 确定链 ID:通过useChainId({ config })获取当前链 ID,若未显式传入chainId,则以当前链为准;
  3. 构造查询选项:调用getEnsAvatarQueryOptions生成enabledqueryFnqueryKey
  4. 执行查询:交给 TanStack Query 的useQuery驱动拉取、缓存与重渲染。

查询键与 enabled 逻辑

查询选项的核心逻辑位于 packages/core/src/query/getEnsAvatar.ts:

return { ...options.query, enabled: Boolean(options.name && (options.query?.enabled ?? true)), queryFn: async (context) => { const [, { scopeKey: _, ...parameters }] = context.queryKey if (!parameters.name) throw new Error('name is required') return getEnsAvatar(config, { ...parameters, name: parameters.name }) }, queryKey: getEnsAvatarQueryKey(options), }

关键点:

  • enabledname强绑定:只要name未提供,查询就不会执行(即使手动设置了query.enabled = true);
  • 查询键格式['ensAvatar', { chainId, name, ... }],由getEnsAvatarQueryKey生成(见 packages/core/src/query/getEnsAvatar.ts#L47-L53)。查询键中包含chainIdname,意味着不同链或不同名称的头像查询互不共享缓存;
  • scopeKey参与缓存隔离scopeKey会保留在查询键中(filterQueryOptions不会过滤它),因此传入不同scopeKey的相同查询会各自独立缓存。

测试用例印证

仓库中的测试 packages/react/src/hooks/useEnsAvatar.test.ts 验证了默认行为:

test('default', async () => { const { result } = await renderHook(() => useEnsAvatar({ name: 'wevm.eth' }), ) await vi.waitUntil(() => result.current.isSuccess, { timeout: 10_000 }) expect(result.current).toMatchInlineSnapshot(` { "data": "https://euc.li/wevm.eth", ... "queryKey": [ "ensAvatar", { "chainId": 1, "name": "wevm.eth", }, ], ... } `) })

该用例确认了两个事实:

  1. wevm.eth查询成功后会返回头像 URL(测试快照中的值为https://euc.li/wevm.eth);
  2. 生成的查询键为["ensAvatar", { chainId: 1, name: "wevm.eth" }],与源码中getEnsAvatarQueryKey的实现一致。

小结

useEnsAvatar是 wagmi 中获取 ENS 头像的推荐方式:它封装了 viem 的底层解析能力,并通过 TanStack Query 提供开箱即用的缓存、重试、依赖查询与 SSR 支持。使用时牢记两点:传入前用normalize规范化 ENS 名称name缺失时查询会被自动禁用。若需在非 React 环境使用同一能力,可直接调用getEnsAvataraction 或从wagmi/query导入查询工具函数。更多相关能力可参考useEnsAddress等 ENS 系列 Hook 的文档。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 3:01:25

STM32F103用SPI模拟并行驱动ILI9225显示屏实战

简介&#xff1a;本资源是一套面向嵌入式开发初学者与STM32工程师的ILI9225显示屏驱动实战代码&#xff0c;聚焦于解决2.0寸SPI接口TFT屏在STM32F103RCT6平台上的适配难题。代码完整实现了SPI模拟并行时序、ILI9225寄存器初始化、像素点绘制、字符显示及基础图形函数&#xff0…

作者头像 李华
网站建设 2026/9/17 3:01:17

RapidSCADA实时数据采集与自定义插件开发实战

做 SCADA 系统集成这些年&#xff0c;我接触最多的开源工控软件就是 RapidSCADA。它的实时数据获取能力、插件扩展机制&#xff0c;比不少商业组态软件还灵活。这篇博文&#xff0c;我就把在 RapidSCADA 上做实时数据采集和插件开发的完整过程&#xff0c;从架构拆解到代码实现…

作者头像 李华
网站建设 2026/9/17 3:00:12

Oracle 11g单库PSU补丁安装全流程详解

做Oracle运维这么多年&#xff0c;我最常被问的问题之一就是&#xff1a;“我这套11g单库&#xff0c;到底要不要打PSU&#xff1f;打补丁会不会出幺蛾子&#xff1f;”说实话&#xff0c;11g虽然“上了年纪”&#xff0c;但在现在的生产环境里存量依然很大&#xff0c;很多企业…

作者头像 李华
网站建设 2026/9/17 2:59:58

uniapp小程序+Vue后台+Node.js+MySQL全栈项目开发实战

简介&#xff1a;一套基于uniapp小程序、Vue后台管理系统与Node.js服务端构成的全栈完整项目&#xff0c;面向需要从零搭建移动端到管理端全流程的小程序开发工程师、前端工程师及全栈学习者。小程序端实现轮播图与招聘车队展示、赛事规则与精彩十佳球查看&#xff0c;用户登录…

作者头像 李华
网站建设 2026/9/17 2:58:07

武汉乡镇面域shp数据处理:WGS84坐标转换与空间关联实战

简介&#xff1a;武汉全市乡镇行政区划面域shp数据&#xff08;WGS-84坐标系&#xff0c;2024最新&#xff09;面向GIS数据处理、城市规划与空间分析人员&#xff0c;用于地图制作、区域查询、面积计算及多源地理数据叠加分析。压缩包内共7个文件&#xff0c;包含shp矢量面域、…

作者头像 李华
网站建设 2026/9/17 2:58:03

OpenClaw工具调用可测试性实践:模拟、注入与回放机制

OpenClaw 开源之后&#xff0c;我注意到圈子里讨论最多的并不是它接了多少个平台、支持多少个模型&#xff0c;而是另一个更现实的问题&#xff1a;大家都在用工具调用&#xff08;Function Calling / Tool Use&#xff09;&#xff0c;但一遇到"模型这次调参数顺序变了&q…

作者头像 李华