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 的更新```javascriptsetCurrent((c) => {// value 可能是值,也可能是函数,inc & dec 传的是函数,set & reset 传的是值const target = typeof value === 'number' ? value : value(c);// 范围判断return getTargetValue(target, {max,min,});});
