You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何自定义usePrevious Hook首次点击按钮时previousValue与counter值相同?

自定义Hook usePrevious首次点击值异常的原因分析

问题现象

你实现的usePrevious Hook在首次点击按钮时,出现previousValue与counter值相同的情况,以下是你的代码:

import { useRef, useState } from "react";
import "./styles.css";

function usePrevious(value) {
  const currRef = useRef();
  const previousValue = currRef.current;
  currRef.current = value;
  return previousValue;
}

export default function App() {
  const [counter, setCounter] = useState(0);
  const previousValue = usePrevious(counter);

  return (
    <div className="App">
      <h1>{counter}</h1>
      <h1>{previousValue}</h1>
      <h2 onClick={() => setCounter((prev) => prev + 1)}>btn</h2>
    </div>
  );
}

成因解析

这个问题的核心是你的usePrevious实现违反了React渲染阶段的纯函数原则,再加上React开发环境严格模式的特殊机制导致的:

1. 渲染阶段直接修改ref的副作用

你在usePrevious中,直接在组件渲染过程中修改currRef.current的值。而React要求组件渲染函数必须是纯函数——不能产生任何副作用(包括直接修改ref),因为渲染阶段的代码可能被React重复执行(比如严格模式下)。

2. 严格模式下的重复渲染检测

在React开发环境的严格模式中,为了帮助开发者发现无意义的副作用,React会对组件函数和effect执行两次。这会导致你的usePrevious逻辑被重复执行,从而意外覆盖ref的值:

首次渲染(两次执行组件函数)

  • 第一次执行:usePrevious(0),currRef.current初始为undefined,返回undefined,随后将currRef.current设为0。
  • 第二次执行:usePrevious(0),此时currRef.current已经是0(第一次执行时设置的),所以返回0,再次将currRef.current设为0。
  • 最终页面显示counter=0、previousValue=0,看起来两者相同。

首次点击后(两次执行组件函数)

  • 状态更新后counter=1,第一次执行:usePrevious(1),currRef.current是上一次的0,返回0,随后设为1。
  • 第二次执行:usePrevious(1),此时currRef.current已经是1(第一次执行时设置的),所以返回1,再次设为1。
  • 最终页面显示counter=1、previousValue=1,也就是你看到的两者相同的情况。

正确的usePrevious实现

应该使用useEffect在渲染完成后的effect阶段更新ref,确保只在渲染结束后执行一次,避免严格模式下的重复执行问题:

import { useRef, useEffect } from "react";

function usePrevious(value) {
  const ref = useRef();
  
  // 仅在value变化且渲染完成后更新ref
  useEffect(() => {
    ref.current = value;
  }, [value]);
  
  return ref.current;
}

正确实现的执行流程

  • 首次渲染:ref.current初始为undefined,渲染完成后useEffect将其设为0,返回undefined。
  • 首次点击后:组件渲染时返回ref.current的旧值0,渲染完成后useEffect将ref.current更新为1,页面显示1和0,符合预期。

内容的提问来源于stack exchange,提问作者Malek Essam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:14:52