Refine Chakra UI<RefreshButton>详解:重新获取当前记录数据及其源码级实现
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
Refine 的 Chakra UI 集成包提供了<RefreshButton>组件,它通过点击按钮重新拉取页面当前展示的记录数据,是 Show/Detail 页面上最常见的“刷新”操作入口。本文以官方文档(refresh.md)为主线,完整讲解其用法与全部属性,并结合当前仓库源码深入剖析其底层调用链——包括useRefreshButton钩子如何定位 resource 与 id、loading状态如何从 React Query 查询状态推导,以及点击后如何通过失效(invalidate)机制触发数据重新获取。
它做了什么
<RefreshButton>基于 Chakra UI 的<Button>组件渲染,点击时触发 refine core 提供的useOne数据方法,进而经由dataProvider重新请求指定 resource 下指定 id 的记录,使页面上已缓存/已展示的数据与服务端最新状态保持一致。
组件支持通过 refine CLI 执行 swizzle(swizzle: true)把源码复制到项目内做二次定制,因此了解它的内部实现对你扩展其行为非常关键。
基本用法
在 Show 页面中,把<RefreshButton />放入<Show>的headerButtons插槽即可获得右上角的刷新按钮。以下示例注册了posts资源,PostShow通过useShow取数,headerButtons中放置刷新按钮(完整可运行示例见原文档中的 live demo 代码块):
import { useShow } from "@pankod/refine-core"; import { Show, Heading, Text, Spacer, MarkdownField, RefreshButton, } from "@pankod/refine-chakra-ui"; const PostShow: React.FC<IResourceComponentsProps> = () => { const { queryResult } = useShow<IPost>(); const { data, isLoading } = queryResult; const record = data?.data; return ( <Show headerButtons={<RefreshButton />} isLoading={isLoading}> <Heading as="h5" size="sm">Id</Heading> <Text mt={2}>{record?.id}</Text> <Heading as="h5" size="sm" mt={4}>Title</Heading> <Text mt={2}>{record?.title}</Text> <Heading as="h5" size="sm" mt={4}>Content</Heading> <Spacer mt={2} /> <MarkdownField value={record?.content} /> </Show> ); };配合Refine组件的resources配置(name: "posts",并挂上show: PostShow),导航到/posts/show/123后,点击按钮即会重新请求id为123的记录。
属性详解
recordItemId:控制刷新哪条记录
recordItemId决定刷新动作作用于哪条数据。不传时,组件默认从路由中读取:id:
import { RefreshButton } from "@pankod/refine-chakra-ui"; const MyRefreshComponent = () => { return <RefreshButton colorScheme="black" recordItemId="123" />; };此时点击按钮会触发useOne,获取 resource 为"post"(取自路由)、id 为"123"的记录。原文档明确提示:<RefreshButton>默认从路由读取 id 信息,因此显式传入recordItemId是覆盖这一默认行为的手段,适合在列表页等无:id路由参数的位置刷新特定记录。
resourceNameOrRouteName:控制刷新哪个资源
resourceNameOrRouteName决定刷新动作作用于哪个资源。同理,不传时默认从路由解析资源名:
const MyRefreshComponent = () => { return ( <RefreshButton colorScheme="black" resourceNameOrRouteName="categories" recordItemId="2" /> ); };此时点击按钮会获取 resource 为"categories"、id 为"2"的记录——即按钮所在的资源(posts)与要刷新的资源(categories)可以不同,这在跨资源联动场景(例如在文章详情页刷新关联分类)中非常有用。
hideText:只显示图标
hideText用于控制按钮文字是否展示。设为true时只渲染按钮图标:
const MyRefreshComponent = () => { return <RefreshButton colorScheme="black" hideText recordItemId="123" />; };从当前仓库的组件源码可以确认其实现方式:hideText为true时渲染的是 Chakra UI 的IconButton(aria-label使用翻译后的 label),否则渲染带leftIcon的Button,见 refresh/index.tsx。
其余 Chakra UI Button 属性
由于RefreshButton的 props 类型是 Chakra UIButtonProps的超集,colorScheme、size、isLoading、className等原生属性都会透传(源码中通过{...rest}展开到<Button>/<IconButton>上),文档示例中的colorScheme="black"即由此而来。此外 Chakra UI 版本还支持svgIconProps用于微调内部刷新图标(默认使用@tabler/icons-react的IconRefresh,尺寸 20)。
API Reference(属性一览)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
recordItemId | BaseKey | 从 URL 的:id读取 | 指定要刷新数据的记录 id |
resourceNameOrRouteName | string | 从路由推断的资源名 | 指定要刷新的资源 |
hideText | boolean | false | 为true时仅显示图标 |
onClick | PointerEventHandler<HTMLButtonElement> | — | 自定义点击处理函数,提供后会替代默认刷新逻辑 |
dataProviderName | string | "default" | 指定目标 data provider(多 data provider 场景) |
children | ReactNode | i18n 的"buttons.refresh"(默认"Refresh") | 按钮文字 |
svgIconProps | Omit<IconProps, "ref"> | — | 透传给刷新图标的 SVG 属性(Chakra UI 专属扩展) |
...rest | Chakra UIButtonProps | — | 透传给底层<Button>/<IconButton> |
说明:
recordItemId/resourceNameOrRouteName的默认行为与上表基于原文档(v3 版本 API)。在当前仓库源码中,v4 起 props 命名演化为resource/recordItemId,且meta已从类型中移除(见下文实现章节)。
源码级实现剖析
组件层:packages/chakra-ui中的薄封装
当前实现位于 RefreshButton 组件,它本身不写任何取数逻辑,而是把全部行为委托给 core 的useRefreshButton钩子,仅负责 UI 呈现:
const { onClick: onRefresh, label, loading, } = useRefreshButton({ resource: resourceNameFromProps, id: recordItemId, dataProviderName, });几个值得注意的实现细节:
- 双形态渲染:
hideText为真时走IconButton分支(附aria-label={label}),否则走Button分支并以leftIcon挂载刷新图标;两个分支都把variant固定为outline。 - loading 直接驱动按钮:
isLoading={loading}让 Chakra UI 按钮自动显示 spinner,无需业务侧手动管理。 - onClick 可被覆盖:
onClick ? onClick(e) : onRefresh()—— 一旦传入自定义onClick,默认刷新逻辑即被完全取代,这为“刷新前先做前置校验”等定制留了口子。 - 可测试性:两个分支都注入了
data-testid={RefineButtonTestIds.RefreshButton}与className={RefineButtonClassNames.RefreshButton}(来自@refinedev/ui-types),供 E2E 与 UI 测试稳定定位。
其 props 类型在 types.ts 中定义为:
export type RefreshButtonProps = RefineRefreshButtonProps< ButtonProps, { svgIconProps?: Omit<IconProps, "ref"> } >;而RefineRefreshButtonProps的通用部分定义在 packages/ui-types/src/types/button.tsx,由RefineButtonCommonProps(含hideText)、RefineButtonResourceProps(含resource,注释明确“默认从路由推断资源名”)、RefineButtonSingleProps(含recordItemId,注释“默认从 URL 读取:id”)、RefineButtonDataProps(含dataProviderName)与RefineButtonLinkingProps(含onClick)组合而成——这正是上文 API 表中各默认行为的类型层依据。
逻辑层:core 中的useRefreshButton
真正的“刷新”逻辑集中在 packages/core/src/hooks/button/refresh-button/index.tsx,三个返回值各有来源:
- resource 与 id 的解析:通过
useResourceParams({ resource, id })把显式传入的 props 与路由参数合并,路由值作为兜底默认——这就是“默认从路由读取 id / 资源名”的实现出处。 label走国际化:translate("buttons.refresh", "Refresh"),接入i18nProvider的应用会自动翻译按钮文字。loading从 React Query 状态推导:
const loading = !!queryClient.isFetching({ queryKey: keys() .data(pickDataProvider(identifier, props.dataProviderName, resources)) .resource(identifier) .action("one") .get(), });即监听one查询是否处于 fetching 状态,一旦取数开始,按钮自动进入 loading,取数结束自动恢复。
- 点击后的动作是“失效”而非直接取数:
const onClick = () => { invalidates({ id, invalidates: ["detail"], dataProviderName: props.dataProviderName, resource: identifier, }); };点击调用useInvalidate使指定 resource/id 的detail类查询失效(stale),由 React Query 自动重新执行对应的one查询并回源到dataProvider。从源码结构看,这与原文档描述的“点击触发useOne重新获取记录”在效果上等价,但机制上已从“按钮直接调用useOne”演进为“失效查询、由缓存层驱动重新取数”——packages/ui-types 的 CHANGELOG 也记录了这次变更:“<RefreshButton />will useuseInvalidatesinstead ofuseOne”,同时meta从RefineRefreshButtonProps中弃用。
版本适用提示:本文档主体对应 v3 版 API(
resourceNameOrRouteName、useOne直取);如果你基于当前仓库源码(v4+)开发,props 应写作resource,刷新机制为 invalidatedetail查询。两者的默认行为(从路由读 id 与资源名)保持一致。
测试保障
Chakra UI 的按钮实现并非单独维护用例,refresh/index.spec.tsx 直接绑定@refinedev/ui-tests包的共享用例集:
import { buttonRefreshTests } from "@refinedev/ui-tests"; describe("Refresh Button", () => { buttonRefreshTests.bind(this)(RefreshButton); });这意味着 refresh 按钮在 Chakra UI、Mantine、Material UI 等各 UI 包中执行同一套行为契约(渲染、点击触发刷新、hideText形态、onClick覆盖等),保证了跨 UI 框架的行为一致性。core 侧同样有对应的 useRefreshButton 测试。
小结与延伸阅读
<RefreshButton>是 Refine “headless 逻辑 + UI 薄封装”架构的典型样本:UI 包(packages/chakra-ui)只负责按钮形态与事件接线,资源/记录的定位、i18n 文案、loading 推导与失效刷新全部沉淀在 core 的useRefreshButton(packages/core/src/hooks/button/refresh-button/index.tsx),并通过@refinedev/ui-types的共享类型与@refinedev/ui-tests的共享用例在多 UI 包间保持一致。常用属性速查:
recordItemId="123":刷新指定 id 的记录(默认取路由:id);resourceNameOrRouteName="categories"(v4+ 为resource):刷新其他资源的记录(默认取路由资源名);hideText:仅保留图标的紧凑形态;- 透传全部 Chakra UI
Button属性,colorScheme="black"等样式写法直接可用。
相关文档:useOne、dataProvider、RefreshButton 原文档。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考