useCounter:计数器
import { useState } from 'react';
import useMemoizedFn from '../useMemoizedFn';
export interface Options {
min?: number;
max?: number;
}
export interface Actions {
inc: (delta?: number) => void;
dec: (delta?: number) => void;
set: (value: number | ((c: number) => number)) => void;
reset: () => void;
}
export type ValueParam = number | ((c: number) => number);
/**
* 处理最大值最小值,保证 value 是在范围内的
*/
function getTargetValue(val: number, options: Options = {}) {
const { min, max } = options;
let target = val;
if (typeof max === 'number') {
target = Math.min(max, target);
}
if (typeof min === 'number') {
target = Math.max(min, target);
}
return target;
}
function useCounter(initialValue: number = 0, options: Options = {}) {
const { min, max } = options;
// useState 可以传一个函数,作为初始值
const [current, setCurrent] = useState(() => {
// 范围判断
return getTargetValue(initialValue, {
min,
max,
});
});
const setValue = (value: ValueParam) => {
setCurrent((c) => {
// value 可能是值,也可能是函数,inc & dec 传的是函数,set & reset 传的是值
const target = typeof value === 'number' ? value : value(c);
// 范围判断
return getTargetValue(target, {
max,
min,
});
});
};
const inc = (delta: number = 1) => {
setValue((c) => c + delta);
};
const dec = (delta: number = 1) => {
setValue((c) => c - delta);
};
const set = (value: ValueParam) => {
setValue(value);
};
const reset = () => {
setValue(initialValue);
};
// 暴露的函数,使用 useMemoizedFn 进行缓存函数,避免重复刷新渲染
return [
current,
{
inc: useMemoizedFn(inc),
dec: useMemoizedFn(dec),
set: useMemoizedFn(set),
reset: useMemoizedFn(reset),
},
] as const;
}
export default useCounter;
- 这里的 useState 初始化 current 时,用的是【函数式更新】,https://zh-hans.reactjs.org/docs/hooks-reference.html#lazy-initial-state ```javascript // useState 可以传一个函数,作为初始值 const [current, setCurrent] = useState(() => { // 范围判断 return getTargetValue(initialValue, { min, max, }); });
// 而不是直接调用函数赋值 const [current, setCurrent] = useState(getTargetValue(initialValue, { min, max, }));
- setCurrent 时,同样也使用的是【函数式更新】,[https://zh-hans.reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update](https://zh-hans.reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update),那么如果说调用 getTargetValue 获取到的值与更新前一致,将会跳过 state 的更新
```javascript
setCurrent((c) => {
// value 可能是值,也可能是函数,inc & dec 传的是函数,set & reset 传的是值
const target = typeof value === 'number' ? value : value(c);
// 范围判断
return getTargetValue(target, {
max,
min,
});
});