useArray
useArray
是一个用于管理数组状态的自定义 Hook,提供了丰富的操作方法(如增、删、改、上下移等),并且在添加元素时会根据指定的唯一键去重,方便管理拥有唯一 ID 或 key 的列表数据。例如,配合 id
字段使用时,添加或合并多个具有相同 id
值的元素会自动去重,避免数据重复。
签名
function useArray<T, K extends keyof T>(initState: T[], key?: K): [
ArrayState<T>,
ArrayActions<T, K>
]
泛型参数
- T:数组元素的类型,如
{ id: number; name: string }
。 - K:用作判定唯一性的属性键,默认为
'id'
(若未显式提供)。
参数
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
initState | T[] | 无 | 数组的初始状态。 |
key | K | undefined | 'id' | 用于去重或查找元素的唯一键,若未提供则会被视为 'id' 。 |
返回值
Hook 返回一个元组,包含两个部分:
- 数组状态:
T[]
- 操作方法:
ArrayActions<T, K>
数组状态
字段 | 类型 | 说明 |
---|---|---|
[0] | T[] | 数组状态,随操作方法而变化。 |
操作方法
字段 | 类型 | 说明 |
---|---|---|
clear | () => void | 清空数组,置为 [] 。 |
down | (itemKey: T[K]) => void | 将指定 itemKey 的元素下移一位。如果元素已在最后一项或找不到,将无操作。 |
findItem | (elementKey: T[K]) => T | undefined | 根据 elementKey 查找并返回对应元素(若不存在则返回 undefined )。 |
pop | () => void | 移除并返回数组的最后一个元素(本方法仅移除,不会返回被移除的元素;效果类似 Array.prototype.pop() )。 |
push | (...newItems: T[]) => void | 向数组尾部追加一个或多个元素,并根据唯一键去重。如果新元素与数组中已有元素的 key 相同,则保留最先出现的元素、忽略后续重复项。 |
remove | (itemKey: T[K]) => void | 根据 itemKey 删除数组中的相应元素。 |
reset | () => void | 重置数组回到初始状态 initState 。 |
reverse | () => void | 反转数组顺序,类似 Array.prototype.reverse() 。 |
shift | () => void | 移除并返回数组的第一个元素(本方法仅移除,不会返回被移除的元素;效果类似 Array.prototype.shift() )。 |
sort | (compareFn?: (a: T, b: T) => number) => void | 按照 compareFn 排序数组(若无对比函数则使用默认排序规则),类似 Array.prototype.sort() 。 |
splice | (start: number, deleteCount?: number, ...items: T[]) => void | 使用类似 Array.prototype.splice() 的操作,在指定位置插入/删除元素。 |
unshift | (...newItems: T[]) => void | 向数组头部插入一个或多个元素,且根据唯一键去重(同 push )。 |
up | (itemKey: T[K]) => void | 将 itemKey 对应的元素上移一位。如果元素已在第一项或找不到,将无操作。 |
updateState | (newState: T[] | ((prevState: T[]) => T[])) => void | 直接更新状态的方法,可传入一个新的数组,或接收前一状态并返回更新后状态的函数。 |
示例
1. 基础用法
import React from 'react';
import useArray from '@/hooks/useArray';
interface User {
id: number;
name: string;
}
export default function Demo() {
const [users, actions] = useArray<User, 'id'>([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]);
const handleAddUser = () => {
actions.push({ id: 3, name: 'Charlie' }, { id: 2, name: 'Bob Duplicate' });
};
return (
<div>
<ul>
{users.map(u => (
<li key={u.id}>
{u.id} - {u.name}
<button onClick={() => actions.remove(u.id)}>删除</button>
</li>
))}
</ul>
<button onClick={handleAddUser}>添加用户(自动去重)</button>
<button onClick={() => actions.up(3)}>将 ID=3 的用户上移</button>
<button onClick={() => actions.down(1)}>将 ID=1 的用户下移</button>
<button onClick={() => actions.clear()}>清空</button>
</div>
);
}
- 点击“添加用户”时会向
users
中追加两个对象:{ id:3, name:'Charlie' }
和{ id:2, name:'Bob Duplicate' }
。由于id:2
和原有Bob
重复,所以最终插入的只有一个id:3
。 - “上移/下移”会根据
id
找到对应对象并调整顺序。
2. 自定义键
interface TodoItem {
key: string;
text: string;
}
const [todoList, todoActions] = useArray<TodoItem, 'key'>([
{ key: 'a', text: 'Do something' },
{ key: 'b', text: 'Buy groceries' }
], 'key');
在这里,唯一键从默认的 'id'
改为 'key'
,内部操作(如 push
、remove
等)都以 item.key
做判定,而非 item.id
。
3. updateState
actions.updateState(prev => {
// 在原有数组基础上,增加一个元素
return [...prev, { id: 4, name: 'David' }];
});
当你需要更灵活、更精细化的更新时,可使用 updateState
传入一个函数,该函数接收当前数组状态并返回新的数组状态。等效于 React 中的 setState(prev => newState)
方式。
结论
useArray
为管理数组类型状态提供了一套常用且实用的操作方法,适用于:
- 需要按照元素中的某个键去重的场景(如
id
、key
等)。 - 增删改查或上移下移之类的排序、位置管理。
- 希望在 React 中使用与原生
Array
类似的操作方法,但又需要更安全、可控的行为。
这样,你可以在组件中轻松编写复杂的列表逻辑,避免重复的数组操作代码,并保持状态管理的简洁与可维护性。
Last updated on