Refine 3.x Chakra UI ShowButton 完整指南:从列表页跳转到详情页的按钮实现与源码剖析
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
ShowButton是 Refine 为 Chakra UI 提供的预置导航按钮组件,用于在列表页中将用户重定向到当前记录的详情(show)页面。本文以 Refine 3.xx 版本文档为核心,结合仓库内@pankod/refine-chakra-ui的真实源码与测试用例,系统讲解其用法、recordItemId、resourceNameOrRouteName、hideText、accessControl等核心属性,并深入剖析其底层基于useNavigation的show方法与useShowButtonhook 的实现原理,帮助你直接复用并可按需 swizzle 定制。
ShowButton 是什么
ShowButton基于 Chakra UI 的<Button>中的show方法完成跳转。它的典型使用场景是:在列表页的"操作"列中,为每一行记录渲染一个"查看详情"按钮,点击后进入/{resource}/show/{id}路由。
从源码实现看(show/index.tsx),ShowButton接收resource、recordItemId、hideText、accessControl、svgIconProps、meta、children、onClick等属性,并将它们传给核心 hookuseShowButton:
const { to, label, title, hidden, disabled, LinkComponent } = useShowButton({ resource: resourceNameFromProps, id: recordItemId, accessControl, meta, });由 hook 计算出的to(目标路由)、label(按钮文案)、title(提示文本)以及hidden/disabled状态,最终决定渲染方式:
hideText为true时,渲染为只带眼睛图标的IconButton;- 否则渲染为带
IconEye左图标的普通Button,按钮文字为children ?? label(默认即 "Show")。
整个按钮被包裹在LinkComponent(即 routerProvider 提供的链接组件)中,因此点击后走的是 SPA 路由跳转而非整页刷新。
快速上手:在列表页使用 ShowButton
以下示例来自 show.md,展示了如何在基于@pankod/refine-react-table的表格中,为每一行渲染一个ShowButton:
import { List, TableContainer, Table, Thead, Tr, Th, Tbody, Td, ShowButton, } from "@pankod/refine-chakra-ui"; import { useTable, ColumnDef, flexRender } from "@pankod/refine-react-table"; const PostList: React.FC = () => { const columns = React.useMemo<ColumnDef<IPost>[]>( () => [ { id: "id", header: "ID", accessorKey: "id", }, { id: "title", header: "Title", accessorKey: "title", }, { id: "actions", header: "Actions", accessorKey: "id", cell: function render({ getValue }) { return <ShowButton recordItemId={getValue() as number} />; }, }, ], [], ); const { getHeaderGroups, getRowModel } = useTable({ columns }); return ( <List> <TableContainer> <Table variant="simple" whiteSpace="pre-line"> <Thead> {getHeaderGroups().map((headerGroup) => ( <Tr key={headerGroup.id}> {headerGroup.headers.map((header) => ( <Th key={header.id}> {!header.isPlaceholder && flexRender( header.column.columnDef.header, header.getContext(), )} </Th> ))} </Tr> ))} </Thead> <Tbody> {getRowModel().rows.map((row) => ( <Tr key={row.id}> {row.getVisibleCells().map((cell) => ( <Td key={cell.id}> {flexRender(cell.column.columnDef.cell, cell.getContext())} </Td> ))} </Tr> ))} </Tbody> </Table> </TableContainer> </List> ); }; interface IPost { id: number; title: string; }在这个示例中,actions列通过getValue()取出当前行的id,并将其作为recordItemId传给ShowButton。别忘了在<Refine>的resources中注册对应的show页面:
const App = () => { return ( <Refine notificationProvider={RefineChakra.notificationProvider()} resources={[ { name: "posts", list: PostList, show: ShowPage, }, ]} /> ); };基础环境配置
文档中的实时示例依赖以下环境配置:使用RefineReactRouterV6作为 routerProvider,RefineSimpleRest作为 dataProvider,并包裹在RefineChakra.ChakraProvider与refineTheme主题之下:
const { default: routerProvider } = RefineReactRouterV6; const { default: simpleRest } = RefineSimpleRest; setRefineProps({ routerProvider, dataProvider: simpleRest("https://api.fake-rest.refine.dev"), Layout: RefineChakra.Layout, Sider: () => null, catchAll: <RefineChakra.ErrorComponent />, });核心属性详解
recordItemId
recordItemId用于将记录 id 追加到路由路径的末尾。例如:
<ShowButton colorScheme="black" recordItemId="123" />点击按钮后会触发useNavigation的show方法,将应用重定向到/posts/show/123。
需要特别注意的是,<ShowButton>默认会从路由中读取 id 信息。也就是说,如果你在 show 页面(如/posts/show/1)上渲染<ShowButton />而不传recordItemId,组件会通过useRouterContext().useParams()自动解析当前路由中的id。这一行为在测试用例中也有明确验证(见下文"路由跳转的测试验证")。
resourceNameOrRouteName
重定向端点(resourceNameOrRouteName/show)由resourceNameOrRouteName属性决定。默认情况下,<ShowButton>使用 resource 对象的name属性作为点击后的跳转端点。当需要跳转到其他资源时,可显式指定:
<ShowButton colorScheme="black" resourceNameOrRouteName="categories" recordItemId="2" />此时点击按钮会触发show方法并跳转到/categories/show/2。对应的resources配置如下:
resources={[ { name: "posts", list: MyShowComponent }, { name: "categories", show: ShowPage }, ]}hideText
hideText用于控制是否显示按钮文字。当为true时,按钮只显示图标(眼睛图标),适合空间紧凑的表格操作列:
<ShowButton colorScheme="black" recordItemId="123" hideText />从源码看(show/index.tsx),hideText模式下渲染IconButton,并透传variant="outline"、aria-label={label}、data-testid等属性,保证无障碍与测试友好。
accessControl
accessControl属性用于控制权限校验行为,仅在向<Refine/>提供了accessControlProvider时生效:
enabled:是否跳过权限校验;hideIfUnauthorized:当用户无权限访问该资源时是否隐藏按钮。
import { ShowButton } from "@pankod/refine-chakra-ui"; export const MyListComponent = () => { return ( <ShowButton accessControl={{ enabled: true, hideIfUnauthorized: true }} /> ); };此外,ShowButton也接受 Chakra UIButton的全部原生属性(colorScheme、size、disabled、hidden、onClick等),这些属性通过...rest透传给底层按钮组件。
底层原理:useShowButton 与 useNavigation
useShowButton 的调用链
ShowButton之所以能拿到跳转目标与权限状态,全部依赖核心包中的useShowButtonhook。从 packages/core/src/hooks/button/index.tsx 可以看到,useShowButton只是对通用导航按钮 hook 的封装:
export const useShowButton = ( props: Prettify<Omit<NavigationButtonProps, "action">>, ) => useNavigationButton({ ...props, action: "show" });即通过action: "show"复用统一的useNavigationButton逻辑,计算路由、权限与文案。类似的还有useEditButton(action: "edit")、useCloneButton、useListButton等,这说明 ShowButton 与 EditButton、CloneButton、ListButton 共享同一套导航按钮基础设施。
useNavigation 的 show 方法
ShowButton的跳转行为最终落到useNavigation的show方法上。根据 useNavigation.md 的说明:
const { show } = useNavigation(); show("posts", "1"); // It navigates to the `/posts/show/1` pageshow方法接收资源名与记录 id,导航到/{resource}/show/{id}页面;它还支持第三个可选参数type(HistoryType),用于控制 push / replace 等导航行为。此外还有配套的showUrl方法,仅返回 URL 而不执行跳转:
const { showUrl } = useNavigation(); showUrl("posts", "1"); // It returns the `/posts/show/1` URL这两者的函数签名分别为:
| 方法 | 说明 | 签名 |
|---|---|---|
show | 跳转到详情页 | ( resource: string, id: BaseKey, type: HistoryType ) => void |
showUrl | 返回详情页 URL | ( resource: string, id: BaseKey ) => string |
路由跳转的测试验证
ShowButton的行为由跨 UI 库共用的测试套件保障。在 packages/ui-tests/src/tests/buttons/show.tsx 中,通过buttonShowTests覆盖了以下关键场景:
- 默认渲染成功,按钮文案为 "Show" 且未被禁用;
- 传入
disabled后按钮禁用,且点击不会触发onClick; - 传入
hidden后按钮不渲染; - 组件带有正确的
data-testid(RefineButtonTestIds.ShowButton); hideText模式下不渲染文字、只显示图标;- 点击按钮触发
onClick回调; - 路由跳转验证:在列表路由
/posts下渲染<ShowButton recordItemId="1" />,点击后链接href为/posts/show/1; - 从详情页读取 id:在
/posts/show/1路由下渲染<ShowButton />(不传 id),生成的链接依然是/posts/show/1,印证了"默认从路由读取 id"的行为; - 自定义资源跳转:
<ShowButton resource="categories" recordItemId="1" />生成/categories/show/1。
这些测试同时覆盖了 accessControl 的多种组合(全局配置 vs 组件属性、enableAccessControl、hideIfUnauthorized、权限拒绝时的禁用与 title 提示等),是理解accessControl语义的第一手资料。Chakra UI 侧的实现只需一行绑定即可接入这套公共测试(show/index.spec.tsx):
import { buttonShowTests } from "@refinedev/ui-tests"; import { ShowButton } from "./"; describe("Show Button", () => { buttonShowTests.bind(this)(ShowButton); });Swizzle 定制与类型说明
使用 refine CLI 进行 swizzle
原文档标记了swizzle: true,意味着你可以使用refine CLI将该组件复制到自己的项目中进行完全定制(例如改变默认图标、调整按钮文案或包装自定义逻辑)。执行 swizzle 后,组件会以可编辑源码的形式落入你的项目,不再受库版本升级影响。
ShowButtonProps 类型
在 packages/chakra-ui/src/components/buttons/types.ts 中,Chakra UI 版的ShowButtonProps定义如下:
export type ShowButtonProps = Omit< RefineShowButtonProps< ButtonProps, { svgIconProps?: Omit<IconProps, "ref">; } >, "ignoreAccessControlProvider" >;也就是说,它继承自@refinedev/ui-types的RefineShowButtonProps,泛型参数为 Chakra UI 的ButtonProps,并额外支持svgIconProps(用于定制眼睛图标的大小与样式,源码中默认size={20})。类型中剔除的ignoreAccessControlProvider用于强制接入权限体系。
总结
ShowButton是 Refine 3.x + Chakra UI 项目中"列表 → 详情"导航的标准解法:
- 开箱即用:无需手写
useNavigation().show(...)与路由拼接,一行<ShowButton recordItemId={id} />即可; - 智能默认:不传
recordItemId时自动从当前路由解析 id,适配列表页与详情页两种使用场景; - 权限友好:配合
accessControlProvider可实现无权限时禁用(并显示原因)或直接隐藏按钮; - 完全可定制:既可通过 Chakra UI 的
ButtonProps自由调整外观,也可通过 refine CLI swizzle 获得源码级控制权。
若需深入了解其底层导航机制,可继续阅读 useNavigation 文档、ShowButton 源码、核心 useShowButton hook 以及 公共按钮测试套件。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考