Skip to content

Commit f0e18b9

Browse files
committed
feat: add useRowSelection hook for managing row selection in tables
1 parent f366bb2 commit f0e18b9

5 files changed

Lines changed: 133 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
# Changelog
44

5+
## 1.7.9
6+
7+
2026-3-18
8+
9+
### Features
10+
11+
- **useRowSelection**
12+
- ✨ Add `useRowSelection` hook for managing row selection in tables.
13+
514
## 1.7.8
615

716
2026-3-18

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tiny-codes/react-easy",
3-
"version": "1.7.8",
3+
"version": "1.7.9",
44
"description": "Simplify React and AntDesign development with practical components and hooks",
55
"keywords": [
66
"react",

src/hooks/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ export { default as useMovable } from './useMovable';
1111
export * from './useProcessingText';
1212
export { default as useProcessingText } from './useProcessingText';
1313

14+
export { default as useRefFunction } from './useRefFunction';
15+
1416
export { default as useRefValue } from './useRefValue';
1517

16-
export { default as useRefFunction } from './useRefFunction';
18+
export * from './useRowSelection';
19+
export { default as useRowSelection } from './useRowSelection';
1720

1821
export * from './useSplitter';
1922
export { default as useSplitter } from './useSplitter';

src/hooks/useRowSelection.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import type { Key } from 'react';
2+
import { useEffect, useMemo, useRef } from 'react';
3+
import type { TableRowSelection } from 'antd/es/table/interface';
4+
5+
export type UseRowSelectionOption<T extends object = Record<string, unknown>> = Omit<
6+
TableRowSelection<T>,
7+
'preserveSelectedRowKeys' | 'selectedRowKeys' | 'onChange'
8+
> & {
9+
/**
10+
* - **EN:** The selected row objects.
11+
* - **CN:** 选中的行对象。
12+
*/
13+
value?: T[];
14+
/**
15+
* - **EN:** Callback function triggered when the selected rows change.
16+
* - **CN:** 当选中的行发生变化时触发的回调函数。
17+
*
18+
* @param value - The selected row objects | 选中的行对象
19+
*/
20+
onChange?: (value: T[]) => void;
21+
/**
22+
* - **EN:** The field name or function to get the key of the object. If not set, the `id` or `code`
23+
* field will be used as the key by default.
24+
* - **CN:** 获取对象key的字段名或函数,如果不设置,则默认使用 `id` 或 `code` 字段作为key。
25+
*/
26+
rowKey?: keyof T | ((item: T) => string);
27+
/**
28+
* - **EN:** Whether to support table selection. If set to `false`, the table row selection feature
29+
* will be disabled.
30+
* - **CN:** 是否支持表格选择,如果设置为`false`,则禁用table的行选择功能。
31+
*
32+
* @default true
33+
*/
34+
checkable?: boolean;
35+
/**
36+
* - **EN:** Cache of all selected objects, used to initialize the internal cache.
37+
*
38+
* This usage is not very common and is generally used for persistent caching outside the Table,
39+
* such as saving in localStorage or, in the case of a MicroApp, saving in the main application,
40+
* so that when the Table is destroyed and re-entered, it can restore all previously selected
41+
* cached objects (otherwise, data across pages will be lost).
42+
* - **CN:** 所有被选中对象的缓存,用于初始化内部的缓存对象。
43+
*
44+
* 这种用法不太常见,一般用于在Table外进行持久化缓存,例如保存在localStorage中、或作为MicroApp时保存在主应用中,
45+
* 从而在Table被销毁后再次进入时能恢复之前选中的所有缓存对象(否则跨页的数据会丢失)。
46+
*/
47+
cache?: T[];
48+
};
49+
50+
/**
51+
* - **EN:** Generate the `rowSelection` property settings for the Table component, supporting
52+
* cross-page selection. The `onChange` callback returns the selected row objects instead of the
53+
* selected row keys.
54+
*
55+
* Use `rowKey` to specify the key of the row object. If not specified, it will try to get the `id`
56+
* or `code` field from the row object as the key.
57+
* - **CN:** 生成Table组件的rowSelection属性设置,支持跨页选中,`onChange`返回选中的行对象,而不是选中的行key。
58+
*
59+
* 使用 `rowKey` 来指定行对象的key,如果不指定,则会尝试从行对象中获取 `id` 或 `code` 字段作为key。
60+
*/
61+
function useRowSelection<T extends object = Record<string, unknown>>(options?: UseRowSelectionOption<T>) {
62+
const { value, rowKey, onChange, checkable = true, cache, ...restOptions } = options || {};
63+
const keys = useMemo(() => value?.map((item) => getKey(item, rowKey)), [value, rowKey]);
64+
const selectedCacheRef = useRef(new Map(cache?.map((item) => [getKey(item, rowKey), item])));
65+
66+
useEffect(() => {
67+
// Remove items that no longer exist in selectedCacheRef
68+
// When the parent component's directory type or domain changes, the value will be reset, and the selectedCacheRef needs to be cleared
69+
selectedCacheRef.current.forEach((item) => {
70+
const itemKey = getKey(item, rowKey);
71+
if (!(value || []).some((v) => getKey(v, rowKey) === itemKey)) {
72+
selectedCacheRef.current.delete(itemKey);
73+
}
74+
});
75+
}, [rowKey, value]);
76+
77+
return checkable !== false
78+
? ({
79+
...restOptions,
80+
preserveSelectedRowKeys: true,
81+
selectedRowKeys: keys,
82+
onChange: (selectedKeys, selectedRows) => {
83+
// Remove the deselected items
84+
selectedCacheRef.current.forEach((item) => {
85+
const key = getKey(item, rowKey);
86+
if (!selectedKeys?.includes(key as Key)) {
87+
selectedCacheRef.current.delete(key);
88+
}
89+
});
90+
// Add new selected items
91+
selectedRows?.forEach((item) => {
92+
const key = getKey(item, rowKey);
93+
selectedCacheRef.current.set(key, item);
94+
});
95+
onChange?.(selectedKeys.map((id) => selectedCacheRef.current.get(id as string)).filter(Boolean) as T[]);
96+
},
97+
} as TableRowSelection<T>)
98+
: undefined;
99+
}
100+
101+
function getKey<T extends object = Record<string, unknown>>(
102+
item: T,
103+
keyField?: keyof T | ((item: T) => string)
104+
): keyof T | string {
105+
if (!item) {
106+
return '';
107+
}
108+
if (typeof keyField === 'function') {
109+
return keyField(item);
110+
}
111+
return (
112+
(item[keyField!] as keyof T) ||
113+
('id' in item ? (item['id'] as string) : '') ||
114+
('code' in item ? (item['code'] as string) : '')
115+
);
116+
}
117+
export default useRowSelection;

0 commit comments

Comments
 (0)