实现 useCounter hook 的优化版本。返回的方法应该被记忆化,在重新渲染时返回相同的函数实例。
export default function Component() {const { count, increment, decrement, reset, setCount } = useCounter();return (<div><p>Counter: {count}</p><button onClick={increment}>Increment</button><button onClick={decrement}>Decrement</button><button onClick={reset}>Reset</button></div>);}
initialValue: number: 计数器状态的初始值。如果未提供,则应默认为 0。useCounter hook 返回一个具有以下属性的 object:
count: number: 当前计数器值increment: () => void: 增加计数器值的函数decrement: () => void: 减少计数器值的函数reset: () => void: 将计数器值重置为 initialValue 的函数,如果未提供则为 0setCount: (value: number) => void: 将计数器值设置为 value 的函数,它与 setState 具有相同的签名increment、decrement、reset 和 setCount 在重新渲染时必须是相同的函数实例。
useCounter 钩子使用 useState 来管理 number 状态。 setter 函数可以根据 useState 钩子的 setValue 来实现。
import { useState } from 'react';export default function useCounter(initialValue = 0) {const [count, setCount] = useState(initialValue);return {count,increment: () => setCount((x) => x + 1),decrement: () => setCount((x) => x - 1),reset: () => setCount(initialValue),setCount,};}
但是,这样编写钩子意味着依赖于实用函数的组件将始终被重新渲染,因为这些函数总是在每次渲染时被重新创建。 为了避免这种情况,我们可以使用 useCallback 来记忆它们。
import { Dispatch, SetStateAction, useCallback, useState } from 'react';interface UseCounterReturn {count: number;increment: () => void;decrement: () => void;reset: () => void;setCount: Dispatch<SetStateAction<number>>;}export default function useCounter(initialValue = 0): UseCounterReturn {const [count, setCount] = useState(initialValue);const increment = useCallback(() => {setCount((x) => x + 1);}, []);const decrement = useCallback(() => {setCount((x) => x - 1);}, []);const reset = useCallback(() => {setCount(initialValue);}, [initialValue]);return {count,increment,decrement,reset,setCount,};}
为了确保一致性,increment 和 decrement 函数使用 更新器函数 来根据先前的值计算新值。 另外,如果您将它们包装在 useCallback 中,则不必将 initialValue 添加到依赖项数组中,并且 increment 和 decrement 将始终被记忆一次。
如果您使用 TypeScript,棘手的部分是确定 setCount 的正确类型,因为它也可以接受更新器函数。 只需将您最喜欢的 IDE 中的 setter 函数从 useState 悬停,您将看到类型签名,形式为 Dispatch<SetStateAction<...>>,其中状态的类型为 ...。 Dispatch 和 SetStateAction 可以从 react 导入。
如果您更喜欢冗长,它本质上可以归结为这样。
type SetCount = (valueOrUpdater: number | ((previousValue: number) => number),) => void;
console.log() 语句将显示在此处。