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

useEffect清理函数未执行:状态更新为何未触发组件卸载日志?

问题解答:React组件状态更新为何未触发清理函数输出?

首先纠正你的核心误解:组件状态改变时只会触发重新渲染,不会卸载后重新挂载。只有当组件从DOM树中被移除时,useEffect的清理函数(也就是你写的console.log("unmounting"))才会执行。

你的代码里出现这个问题的原因有两点:

1. useEffect的依赖数组导致闭包陷阱

你给useEffect传了空依赖数组[],这意味着这个effect只会在组件首次挂载时执行一次。定时器的回调函数捕获的是初始状态下的count值(也就是0),所以每次定时器触发时,执行的都是setCount(0 + 1)——当count变成1之后,后续的定时器调用不会再改变count的值,组件也不会再触发重新渲染。

2. 清理函数的执行时机不对

这个effect的清理函数只有在组件卸载时才会被调用,而你的组件始终处于挂载状态,只是状态更新触发了一次重新渲染,所以清理函数根本没机会执行,控制台自然不会输出"unmounting"。

两种修复方案

方案一:添加count作为useEffect的依赖

import React, { useState, useEffect } from "react";

function HookCounter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => setCount(count + 1), 1000);
    return () => {
      console.log("更新前清理旧定时器");
      clearInterval(interval);
    };
  }, [count]); // 添加count作为依赖
  return <div>{count}</div>;
}

每次count更新时,旧的effect会先执行清理函数(输出日志),然后新的effect会创建新的定时器。不过这种方式会频繁销毁和重建定时器,性能上略有损耗。

方案二:使用函数式更新避免闭包问题

import React, { useState, useEffect } from "react";

function HookCounter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => setCount(prevCount => prevCount + 1), 1000);
    return () => {
      console.log("组件卸载时清理定时器");
      clearInterval(interval);
    };
  }, []); // 保持空依赖,函数式更新无需依赖count
  return <div>{count}</div>;
}

这里用setCount(prevCount => prevCount + 1),每次更新都会基于上一次的状态值计算新状态,不需要依赖count变量。effect只会执行一次,清理函数也只会在组件卸载时触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 02:32:51