useCounter:计数器

  1. import { useState } from 'react';
  2. import useMemoizedFn from '../useMemoizedFn';
  3. export interface Options {
  4. min?: number;
  5. max?: number;
  6. }
  7. export interface Actions {
  8. inc: (delta?: number) => void;
  9. dec: (delta?: number) => void;
  10. set: (value: number | ((c: number) => number)) => void;
  11. reset: () => void;
  12. }
  13. export type ValueParam = number | ((c: number) => number);
  14. /**
  15. * 处理最大值最小值,保证 value 是在范围内的
  16. */
  17. function getTargetValue(val: number, options: Options = {}) {
  18. const { min, max } = options;
  19. let target = val;
  20. if (typeof max === 'number') {
  21. target = Math.min(max, target);
  22. }
  23. if (typeof min === 'number') {
  24. target = Math.max(min, target);
  25. }
  26. return target;
  27. }
  28. function useCounter(initialValue: number = 0, options: Options = {}) {
  29. const { min, max } = options;
  30. // useState 可以传一个函数,作为初始值
  31. const [current, setCurrent] = useState(() => {
  32. // 范围判断
  33. return getTargetValue(initialValue, {
  34. min,
  35. max,
  36. });
  37. });
  38. const setValue = (value: ValueParam) => {
  39. setCurrent((c) => {
  40. // value 可能是值,也可能是函数,inc & dec 传的是函数,set & reset 传的是值
  41. const target = typeof value === 'number' ? value : value(c);
  42. // 范围判断
  43. return getTargetValue(target, {
  44. max,
  45. min,
  46. });
  47. });
  48. };
  49. const inc = (delta: number = 1) => {
  50. setValue((c) => c + delta);
  51. };
  52. const dec = (delta: number = 1) => {
  53. setValue((c) => c - delta);
  54. };
  55. const set = (value: ValueParam) => {
  56. setValue(value);
  57. };
  58. const reset = () => {
  59. setValue(initialValue);
  60. };
  61. // 暴露的函数,使用 useMemoizedFn 进行缓存函数,避免重复刷新渲染
  62. return [
  63. current,
  64. {
  65. inc: useMemoizedFn(inc),
  66. dec: useMemoizedFn(dec),
  67. set: useMemoizedFn(set),
  68. reset: useMemoizedFn(reset),
  69. },
  70. ] as const;
  71. }
  72. export default useCounter;

// 而不是直接调用函数赋值 const [current, setCurrent] = useState(getTargetValue(initialValue, { min, max, }));

  1. - 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 的更新
  2. ```javascript
  3. setCurrent((c) => {
  4. // value 可能是值,也可能是函数,inc & dec 传的是函数,set & reset 传的是值
  5. const target = typeof value === 'number' ? value : value(c);
  6. // 范围判断
  7. return getTargetValue(target, {
  8. max,
  9. min,
  10. });
  11. });