react-admin 中的 useUnselectAll精通数据表格的全量取消选择机制【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin导读useUnselectAll是 react-adminra-core提供的一个 Hook用于返回一个“取消全部选择”的函数可一键清空DataTable/Datagrid等列表控件中的整页选中状态。在批量操作如批量删除、批量更新完成、或数据被删除导致选中记录失效时该 Hook 能帮助你精确地重置 Store 中持久化的selectedIds状态。读完本文你将掌握useUnselectAll的完整 API、与 Store 的同步机制、跨 storeKey 清理选中态的原理以及它在 react-admin 源码中的真实实现路径。概述这个 Hook 解决什么问题在 react-admin 的列表页中用户通过复选框选中的记录 ID 会被写入全局 Store持久化键名默认为${resource}.selectedIds。react-admin 提供了多个与此状态协同工作的 HookuseUnselectAll清空当前资源在某一个 storeKey 下或全部 storeKey 下的选中记录useUnselect按 ID 列表移除指定记录的选择状态useSelectAll拉取当前筛选条件下的全部记录并一次性选中默认上限 250 条useRecordSelection上述 Hook 的底层基础直接暴露select、unselect、toggle、clearSelection四个修改器。useUnselectAll的定位非常单一它专门封装clearSelection适合用在“取消全部选择”按钮、或批量操作完成后的清理逻辑中。其导出定义位于 useUnselectAll.ts完整源码只有 25 行逻辑清晰、开箱即用。基本用法一行代码接入取消全部选择官方文档给出的是一个标准的自定义按钮组件示例。假设你正在构建一个Datagrid的自定义批量操作工具栏可以这样实现import { useListContext, useUnselectAll } from ra-core; const UnselectAllButton () { const { resource } useListContext(); const unselectAll useUnselectAll(resource); const handleClick () { unselectAll(); }; return button onClick{handleClick}Unselect all/button; };要点说明useListContext()提供当前列表的上下文信息其中resource是当前资源名如postsuseUnselectAll(resource)返回一个可直接调用的函数unselectAll点击按钮调用unselectAll()即可清空当前资源的选择状态。如果你的组件不在ListContext内部也可以从react-admin顶层包导入经典文档 docs/useUnselectAll.md 中演示的导入方式用法完全一致import { useListContext, useUnselectAll } from react-admin;参数详解resource 与 storeKeyuseUnselectAll接受两个可选参数签名定义在 useUnselectAll.tsexport const useUnselectAll (resource?: string, storeKey?: string) { ... }resource指定资源名传入resource时Hook 会将该资源的选择状态与全局 Store 同步import { useListContext, useUnselectAll } from ra-core; const UnselectAllButton () { const { resource } useListContext(); const unselectAll useUnselectAll(resource); const handleClick () { unselectAll(); }; return button onClick{handleClick}Unselect all/button; };不传resource仅操作本地状态如果省略resourceHook 将只更新本地内存中的选择状态不会写入 Store。这在父级列表设置了storeKey为false即禁用与 Store 的同步时非常有用import { useUnselectAll } from ra-core; const UnselectAllButton () { // 不传参数——仅本地选择状态不持久化 const unselectAll useUnselectAll(); const handleClick () { unselectAll(); }; return button onClick{handleClick}Unselect all/button; };这一点在源码中有非常明确的体现。看 useUnselectAll.ts 的实现const [, { clearSelection }] useRecordSelection( resource ? { resource, storeKey } : { disableSyncWithStore: true, storeKey } );当resource缺失时会向底层的useRecordSelection传入disableSyncWithStore: true从而切换到纯本地状态local state模式。这与 useListController.ts 中storeKey false时设置disableSyncWithStore: true的行为保持一致。使用提示请务必让useUnselectAll的调用方式与父级控制器保持一致——如果父级ListBase/List设置了storeKey为false那么这里也应不传resource或保持相同的禁用策略否则会出现“清不干净”或“跨页面残留”的状态错乱。storeKey自定义存储键默认情况下storeKey 由资源名推导而来${resource}.selectedIds。你可以在第二个参数中传入自定义storeKey来覆盖它但必须与父级控制器如useListController或ListBase使用的storeKey一致否则两个组件将读写不同的 Store 键导致选中状态互不可见。最终写入 Store 的键为${storeKey}.selectedIdsimport { useListContext, useUnselectAll } from ra-core; const UnselectAllButton () { const { resource } useListContext(); const unselectAll useUnselectAll(resource, customStoreKey); const handleClick () { unselectAll(); }; return button onClick{handleClick}Unselect all/button; };关于storeKey与资源名组合的键规则可以追溯到底层 useRecordSelection.tsconst finalStoreKey ${storeKey || resource}.selectedIds;也就是说传入参数实际 Store 键resourceposts未传 storeKeyposts.selectedIdsresourcepostsstoreKeycustomStoreKeycustomStoreKey.selectedIds因此同一个资源可以拥有多个互不干扰的选中状态例如同一页面内的多张表格只要它们使用不同的storeKey。返回值支持跨 storeKey 清理的清理函数useUnselectAll返回一个函数该函数接受一个可选布尔参数fromAllStoreKeys默认false。置为true时会清空该资源在全部 storeKey 下的选中记录而不只是当前 storeKey 对应的那份。典型场景当你执行了删除操作如批量删除被删除的记录不应继续停留在任何其他列表的选中状态中此时应当传trueimport { useListContext, useUnselectAll } from ra-core; const UnselectAllButton () { const { resource } useListContext(); const unselectAll useUnselectAll(resource); const handleClick () { // 跨所有 storeKey 取消选择 unselectAll(true); }; return button onClick{handleClick}Unselect all/button; };源码层面的“跨 storeKey”实现fromAllStoreKeys的语义在 useRecordSelection.ts 的clearSelection中落地clearSelection: (fromAllStoreKeys?: boolean) { setStore(defaultIds); if (!disableSyncWithStore fromAllStoreKeys) { storeKeys .filter(storeKey storeKey ! finalStoreKey) .forEach(storeKey { const ids getItemRecordType[id][](storeKey); if (ids) { setItemRecordType[id][](storeKey, defaultIds); } }); } },执行流程可以拆解为三步先把当前 storeKey对应的选中数组重置为[]即源码中的defaultIds若fromAllStoreKeys为true且未禁用 Store 同步则遍历 Store 中记录的所有其他 storeKey对其他 storeKey 下已存在的选中数组逐一用setItem重置为空数组。那么“有哪些 storeKey 属于当前资源”是如何被追踪的答案在useRecordSelection的副作用逻辑里。当传入storeKey且启用了 Store 同步时Hook 会把最终键名登记到${resource}.selectedIds.storeKeys数组中见 useRecordSelection.tsconst [storeKeys, setStoreKeys] useStorestring[]( ${resource}.selectedIds.storeKeys, defaultStoreKeys );这就是fromAllStoreKeys能“精准扫射”同一资源所有列表选中态的关键机制clearSelection(true)会读取${resource}.selectedIds.storeKeys过滤掉当前键后依次清理。测试用例 useRecordSelection.spec.tsx 中should check all storeKeys listed in store when fromAllStoreKeys is true等用例对该行为做了完整验证。底层机制useRecordSelection 与 Store 键推导useUnselectAll本身是useRecordSelection的薄封装。useRecordSelection的返回值是一个元组[selectedIds, { select, unselect, toggle, clearSelection }]类型定义见 useRecordSelection.tsuseUnselectAll只解构出其中的clearSelection并用useCallback稳定化后返回见 useUnselectAll.tsreturn useCallback( (fromAllStoreKeys?: boolean) { clearSelection(fromAllStoreKeys); }, [clearSelection] );理解这一层封装关系可以帮助你判断何时该用useUnselectAll、何时该直接用useRecordSelection只关心“一键清空全部选中”就用useUnselectAll需要同时管理选择、取消选择、切换等完整操作直接用useRecordSelection需要按 ID 精确移除某些行用useUnselect其实现与useUnselectAll完全对称见 useUnselect.ts需要“全选当前筛选结果”用useSelectAll见 useSelectAll.tsx内部通过dataProvider.getList拉取最多limit默认 250条记录并调用select。所有这些 Hook 都在 controller/list/index.ts 中统一导出其中就包括useRecordSelection、useUnselect、useUnselectAll与useSelectAll。与 DataTable / Datagrid 的联动实践官方文档docs_headless 与 docs/useUnselectAll.md都强调该 Hook 用于DataTableBase/DataTable的行选择清理。一个典型的批量删除完整闭环如下import { useListContext, useUnselectAll, useDeleteMany, useNotify, } from ra-core; const BulkDeleteThenUnselectAllButton () { const { resource, selectedIds } useListContext(); const unselectAll useUnselectAll(resource); const notify useNotify(); const [deleteMany, { isPending }] useDeleteMany(resource, { ids: selectedIds, mutationMode: pessimistic, }); const handleClick () { deleteMany( {}, { onSuccess: () { // 记录已删除跨所有 storeKey 清理防止残留选中态 unselectAll(true); notify(ra.notification.deleted, { type: info, messageArgs: { smart_count: selectedIds.length }, }); }, } ); }; return button onClick{handleClick}Delete Unselect/button; };这个示例中有两个值得注意的工程细节删除成功后必须清理选中态否则已删除记录的 ID 仍残留在${resource}.selectedIds中刷新列表或切换分页时会出现幽灵选中传true保证跨列表一致性当同一资源在多个列表多 storeKey中同时出现时unselectAll(true)才能一次性清理干净。边界情况与常见陷阱结合源码实现useRecordSelection.ts和测试useRecordSelection.spec.tsx以下几点值得注意空选择不报错clearSelection对空数组、空 Store 项都做了防御性处理。测试用例should not fail on empty selection验证了直接清空空选择不会抛出异常。fromAllStoreKeys仅在启用 Store 同步时生效如果走的是disableSyncWithStore: true的本地模式即未传resourceclearSelection只会重置本地 statefromAllStoreKeys的跨键清理逻辑会被短路跳过见 useRecordSelection.ts 的!disableSyncWithStore fromAllStoreKeys条件。storeKey 必须前后一致useUnselectAll的第二个参数必须与父级ListBase/useListController的storeKey完全一致否则清空操作会作用在错误的 Store 键上。useListController对storeKey的接收与disableSyncWithStore推导逻辑见 useListController.ts。Hook 是轻量且稳定的useUnselectAll通过useCallback缓存返回函数依赖项只有clearSelection不会因父组件重渲染而产生新的函数引用适合直接作为子组件 props 传递。总结useUnselectAll是 react-admin 列表选择状态体系中最直接的“清空入口”两个可选参数resource指定资源缺省时仅操作本地状态与storeKey自定义存储键默认${resource}.selectedIds一个返回函数可传fromAllStoreKeys布尔值true时跨所有 storeKey 清理适配删除类场景底层依托useRecordSelection的clearSelection并通过${resource}.selectedIds.storeKeys追踪同一资源的全部选中键位。掌握它你就掌握了 react-admin 批量操作后选中态清理的标准姿势。若要继续深入推荐依次阅读 useRecordSelection.ts核心状态与修改器实现、useRecordSelection.spec.tsx行为契约测试以及同目录下的 useUnselect.ts 与 useSelectAll.tsx选择操作三件套的另外两件。【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
