react-use `usePrevious` 深入解析:如何用 Hooks 优雅获取上一次的 state 与 props
react-useusePrevious深入解析如何用 Hooks 优雅获取上一次的 state 与 props【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-use导读usePrevious是 react-use 中一个极简却高频使用的 State 类 Hooks用于在函数组件中获取上一次渲染时的 state 或 props——这是 React 官方 Hooks FAQ 中反复提及的经典需求。本文以 docs/usePrevious.md 为主线结合 src/usePrevious.ts 源码与 tests/usePrevious.test.ts 测试用例讲解其原理、用法、边界行为并对比同为 previous 家族的usePreviousDistinct的差异读完即可在项目中直接落地使用。为什么需要usePrevious类组件时代的能力缺口在 React 类组件中我们可以借助componentDidUpdate(prevProps, prevState)拿到上一次的 props 与 state进而做变化后触发副作用之类的比较逻辑。而函数组件中的useState/useProps只提供当前值React 官方 Hooks FAQ 专门给出了如何获取上一次的 props 或 state的经典实现即用useRef在每次渲染后缓存旧值。usePrevious正是把这一官方思路封装成可直接复用的 Hook。典型应用场景包括比较新旧值判断值是否发生变化从而触发动画、日志或数据请求实现从 A 状态切到 B 状态时读取 A 作为参照在受控组件中对比上一次的输入值与当前输入值。使用方式最小可运行的 Demo按照 docs/usePrevious.md 的用法从react-use包中导入即可import {usePrevious} from react-use; const Demo () { const [count, setCount] React.useState(0); const prevCount usePrevious(count); return ( p button onClick{() setCount(count 1)}/button button onClick{() setCount(count - 1)}-/button p Now: {count}, before: {prevCount} /p /p ); };点击/-按钮时count更新而prevCount始终停留在上一次渲染时的值第一次渲染count为 0、prevCount为undefined点击一次后count变为 1、prevCount变为 0。页面因此可以同时展示当前值与上一次的值。该 Hook 位于react-use的 State 分类下在 README.md 的目录中与usePreviousDistinct并列并在 src/index.ts 中被统一导出export { default as usePrevious } from ./usePrevious;因此也可以按需导入。Reference签名与类型原文档给出的类型签名如下const prevState usePrevious T(state: T): T;需要指出的是实际源码 src/usePrevious.ts 的返回类型更精确是T | undefinedexport default function usePreviousT(state: T): T | undefined { const ref useRefT(); useEffect(() { ref.current state; }); return ref.current; }参数state是任意泛型类型T基本类型、对象、数组均可返回值类型为T | undefined因为首次渲染时上一次的值尚不存在。实现原理useRefuseEffect的黄金组合整个实现只有 4 行核心逻辑拆解如下const ref useRefT();创建一个 ref 容器初始值为undefined用来跨渲染保存上一次的值。ref 对象在组件整个生命周期内保持同一个引用且修改ref.current不会触发重新渲染。useEffect(() { ref.current state; });注意这里没有传依赖数组因此每次渲染完成后都会执行把当次渲染的state写入ref.current。return ref.current;返回上一次渲染写入的旧值。关键时序在于useEffect的清理与执行发生在浏览器完成绘制之后commit 阶段之后而函数组件体含return ref.current先于 effect 执行。因此渲染期间读到的永远是上一次渲染结束时所保存的值而在本次渲染完成后effect 才把新值写入 ref供下一次渲染读取。这一先读旧值、后写新值的错峰机制正是获取 previous 值的核心。这与官方 FAQ 给出的手写方案完全同构相当于把 FAQ 中的样板代码收敛为一个类型完备、开箱即用的 API。边界行为首次渲染返回undefined由于初始useRefT()没有传入初始值首次渲染时ref.current为undefined且此时 effect 尚未执行所以首次渲染usePrevious一定返回undefined。这一点在测试 tests/usePrevious.test.ts 中有明确断言it(should return undefined on initial render, () { const { result } setUp(); expect(result.current).toBeUndefined(); });因此在使用时若对返回值做运算如prev.value建议先判空文档示例之所以直接渲染{prevCount}是因为 React 对undefined渲染为空恰好安全。每次更新都返回严格上一次的值测试同时验证了连续多次更新的行为tests/usePrevious.test.tsit(should always return previous state after each update, () { const { result, rerender } setUp(); rerender({ state: 2 }); expect(result.current).toBe(0); rerender({ state: 4 }); expect(result.current).toBe(2); rerender({ state: 6 }); expect(result.current).toBe(4); });可见它遵循每次渲染都推进一位的语义渲染序列 0 → 2 → 4 → 6 时返回值依次是undefined→ 0 → 2 → 4。即使新值与旧值完全相同只要发生了重新渲染返回的 previous 也会更新——因为 effect 无条件执行没有任何去重判断。这是与usePreviousDistinct的本质区别。相关对比usePreviousvsusePreviousDistinct仓库中还提供了usePreviousDistinctsrc/usePreviousDistinct.ts其签名允许传入自定义谓词export type PredicateT (prev: T | undefined, next: T) boolean; export default function usePreviousDistinctT( value: T, compare: PredicateT strictEquals ): T | undefined区别要点维度usePrevioususePreviousDistinct更新时机每次渲染都推进 previous仅当compare(prev, next)判定为不同时才推进默认比较无条件更新严格相等prev next相等时不更新实现方式useEffect延迟写入渲染期直接比较并更新 refsrc/usePreviousDistinct.ts并借助useFirstMountStatesrc/useFirstMountState.ts跳过首次挂载典型场景简单获取上一次值只需在值真正变化时记录避免无谓的 previous 刷新例如记录上一次不同的搜索关键词这类需求用usePreviousDistinct更合适而严格跟随每次渲染的通用场景usePrevious更直接。两个 Hook 在 README.md 中被并列介绍互为补充。注意事项与最佳实践首次渲染为undefined类型签名是T | undefined消费前注意空值处理它不会触发重渲染previous 的更新依附于state自身的更新usePrevious本身不会引起额外的渲染比较逻辑需自行完成usePrevious不提供任何是否变化的判断若要实现值变化时才做某事请自行用!比较state与prevState或直接改用usePreviousDistinct对引用类型同样有效传入对象、数组时返回的是上一次渲染时的引用可用Object.is或深比较来判断内容是否变化仓库中另有usePreviousDistinct的谓词可自定义比较策略在 Storybook 中的演示仓库的 stories/usePrevious.story.tsx 展示了与文档一致的计数演示prevCount展示并作为State/usePrevious故事组挂在 Docs 页面下方便在本地 Storybook 中交互验证行为。小结usePrevious用 4 行源码实现了 React 官方 FAQ 中最经典的获取上一次值方案useRef承载跨渲染缓存无依赖的useEffect在每次渲染后推进旧值从而让函数组件拥有了类组件componentDidUpdate般的前后对比能力。配合首次渲染返回undefined的语义、每次更新严格推进的行为以及与usePreviousDistinct的差异化选择足以覆盖绝大多数需要知道上一次值的业务场景。【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-use创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →