如何编写自定义React Hook:仅在值变化时更新状态
实现带自定义比较逻辑的useState Hook
下面是一个符合需求的自定义Hook,它和原生useState用法一致,但会先通过比较函数判断新旧值是否相等,只有当值真正变化时才更新状态:
import { useState, useRef, useCallback } from 'react'; function useCompareState(initialValue, compareFn = (a, b) => a === b) { const [state, setState] = useState(initialValue); const prevValueRef = useRef(initialValue); const setCompareState = useCallback((newValue) => { // 兼容useState的函数式更新写法 const resolvedValue = typeof newValue === 'function' ? newValue(state) : newValue; // 调用比较函数判断新旧值是否相等 const valuesEqual = compareFn(prevValueRef.current, resolvedValue); if (!valuesEqual) { prevValueRef.current = resolvedValue; setState(resolvedValue); } }, [compareFn, state]); return [state, setCompareState]; }
核心逻辑说明
- 初始值:第一个参数
initialValue和原生useState完全一致,可以是直接值,也可以是返回初始值的函数(用于复杂初始化场景) - 自定义比较:第二个可选参数
compareFn默认用===做浅比较;你可以传入自定义函数,只要函数返回true就认为新旧值相等,不触发状态更新 - 状态缓存:用
useRef存储上一次的状态值,确保每次比较时能拿到准确的旧值 - 函数稳定性:用
useCallback包裹更新函数,避免因依赖变化以外的原因导致函数重新创建,减少不必要的组件重渲染 - 兼容函数式更新:支持像原生
useState那样传入函数作为更新参数,比如setCompareState(prev => prev + 1)
使用示例
场景1:默认浅比较
适合基础类型(数字、字符串、布尔值)的状态判断:
function Counter() { const [count, setCount] = useCompareState(0); const handleClick = () => { setCount(0); // 和当前值相等,不会触发更新 setCount(count + 1); // 值变化,触发更新 }; return ( <div> <p>当前计数:{count}</p> <button onClick={handleClick}>更新计数</button> </div> ); }
场景2:自定义对象比较
当状态是对象时,可以通过比较特定字段判断是否需要更新:
function UserCard() { const [user, setUser] = useCompareState( { id: 1, name: '张三' }, (prevUser, newUser) => prevUser.id === newUser.id ); const updateUser = () => { // 相同id,不触发更新 setUser({ id: 1, name: '张三(更新)' }); // 不同id,触发更新 setUser({ id: 2, name: '李四' }); }; return ( <div> <p>用户信息:{JSON.stringify(user)}</p> <button onClick={updateUser}>更新用户</button> </div> ); }
内容的提问来源于stack exchange,提问作者McKrus
相关产品推荐
相关产品推荐

