前言
ahooks是阿里开源的一个react hooks 工具库,其中包含了许多业务上能用到的自定义hooks。本文会介绍其中一个hooks —— useHistoryTravel
的使用场景和讲解代码实现。
使用场景
从命名上看,它叫历史旅游,是用来管理状态变化历史。可以快速在状态变化历史中穿梭,方便实现撤销跟重做操作。
核心特性
● 撤销
● 重做
● 状态管理
实现
工具函数
useHistroyTravel
内部用到2个自定义的工具函数:dumpIndex
和split
。
dumpIndex
dumpIndex
接收一个数字step和arr数组,内部根据step的值和arr的大小生成一个index并返回。
● 正数处理:如果step > 0,index = step - 1 (为什么要减1,后面会讲)
● 负数处理:如果step < 0 , index = arr.length + step
● 边界限制: 0 <= index <= arr.length - 1
const dumpIndex = <T>(step: number, arr: T[]) => {
let index =
step > 0
? step - 1 // move forward
: arr.length + step; // move backward
if (index >= arr.length - 1) {
index = arr.length - 1;
}
if (index < 0) {
index = 0;
}
return index;
};
split
从函数命名就可以看出来了,这个函数的功能就是分裂,根据index把目标数组分成 过去、当前、未来 三部分。
const split = <T>(step: number, targetArr: T[]) => {
const index = dumpIndex(step, targetArr); //使用dumpIndex获取限制后的index
return {
_current: targetArr[index],
_before: targetArr.slice(0, index),
_after: targetArr.slice(index + 1)
}
}
从代码中可以看出,dumpIndex
得到的index是作为数组下标来使用的,所以也能解释为什么dumpIndex
中step大于0需要减1。
类型
useHistoryTravel
类型还是比较简单的,只有一个IData
。
interface IData<T> {
present?: T; // 当前的值
past: T[]; // 保存过去值的数组
future: T[]; // 保存未来值的数组
}
设计
useHistoryTravel
内部结构很清晰,如下图:
present
是初始值,past
和future
一开始是空数组。
接下来看看每个操作函数具体怎么做。
updateValue
首先是updateValue
,对外命名是setValue
。
updateValue
的作用是更新value
,需要改变当前的present
,并且把当前的present
保存到past
中。
const updateValue = useCallback((val: T) => {
setHistory({
present: val,
future: [],
past: [...past, present]
})
},[history, setHistory]);
_backward & _forward
_backward
,后退,默认后退一步。可根据入参step
后退多少步,该函数不直接对外。
const _backward = useCallback(
(step: number = -1) => {
if (past.length === 0) {
return;
}
const { _before, _current, _after } = split(step, past);
setHistory({
past: _before,
present: _current,
future: [..._after, present, ...future]
});
},
[history, setHistory]
);
_forward
,前进,默认前进一步。可根据入参step前进多少步,该函数不直接对外,和_backward
一起被go
函数使用。
const _forward = useCallback(
(step: number = 1) => {
if (future.length === 0) {
return;
}
const { _before, _current, _after } = split(step, future);
setHistory({
past: [...past, present, ..._before],
present: _current,
future: _after
});
},
[history, setHistory]
);
go
go
函数执行状态跳转操作,内部根据入参step
来决定使用_backward
还是_forward
。
const go = useCallback(
(step: number) => {
const stepNum = typeof step === 'number' ? step : Number(step);
if (stepNum === 0) {
return;
}
if (stepNum > 0) {
return _forward(stepNum);
}
_backward(stepNum);
},
[_backward, _forward]
);
reset
reset
是用来重置当前history的,可以重置到初始值或者提供一个新的初始值。
const reset = useCallback(
(...params: any[]) => {
const _initial = params.length > 0 ? params[0] : initialValueRef.current;
initialValueRef.current = _initial;
setHistory({
present: _initial,
future: [],
past: []
});
},
[history, setHistory]
);
总结
useHistoryTravel
代码结构清晰,功能完善,如果业务中有需要管理状态变化历史,可以放心使用。其实react
中有一个hooks useReducer
,使用useReducer
来维护状态和集中管理action也能实现类似useHistoryTravel
的功能。
网友评论