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

为何useEffect清理函数无法获取最新state值?React状态管理解惑

问题:React useEffect清理函数中无法获取更新后的state值

我本学期正在学习React,对相关概念完全陌生。我编写了一段代码,用于获取用户头像并通过createObjectURL展示,同时在页面跳转时释放该URL。问题在于,尽管网站上头像已成功展示,但在useEffect的清理函数中,avatarURL并未保存获取到的头像URL(在调用revokeObjectURL前打印验证过)。

我尝试创建按钮打印URL,结果符合预期。我已了解state更新是异步的,且确认在调用清理函数(页面跳转)前state已成功更新并正确渲染头像,但清理函数中仍获取到旧值。我想了解:React是如何管理状态的?为何同一个“变量”在不同调用中会有不同的值?

代码示例

// State declaration
const [avatarURL, setAvatarURL] = useState<string>(userDummyPFP); 

// Fetching API call
function fillPageData() {
  fetch("http://localhost:3000/get-user-pfp", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      userID: "UID-2",
    }),
    credentials: "include",
  })
    .then((response) => response.blob())
    .then((data) => {
      const img = URL.createObjectURL(data);
      setAvatarURL(img);
      setTest(img);
    })
    .catch((err) => {
      console.log(err);
    });
}

// Run the fetch one first time + return clean-up callback
useEffect(() => {
  const abortController = new AbortController();

  fillPageData();

  return () => {
    console.log(avatarURL);
    URL.revokeObjectURL(avatarURL); 
    abortController.abort();
  };
}, []);

核心原因:闭包捕获的是state的快照

React组件的每次渲染都是基于状态快照的:当state更新时,组件会重新执行一遍,生成一个全新的函数作用域,这个作用域里的state变量是当前更新后的快照值。

你的useEffect依赖项是空数组[],意味着它只会在组件首次挂载时执行一次。此时创建的清理函数会牢牢抓住首次挂载作用域里的avatarURL值——也就是初始的userDummyPFP。后续setAvatarURL更新state时,组件虽然重新渲染,但首次挂载时生成的清理函数不会被更新,它依然持有旧作用域里的avatarURL,所以页面跳转触发清理时,打印的还是初始值。

而按钮能打印正确值,是因为按钮的点击事件是在组件重新渲染后的新作用域里创建的,捕获的是更新后的avatarURL快照。

解决方案:用useRef保存最新值

要让清理函数拿到最新的avatarURL,可以用useRef来存储这个值。ref的current属性是可变的,不会受组件渲染快照的限制,始终能拿到最新值:

import { useState, useEffect, useRef } from 'react';

const [avatarURL, setAvatarURL] = useState<string>(userDummyPFP);
// 用ref同步保存最新的avatarURL
const avatarURLRef = useRef<string>(userDummyPFP);

// 每当avatarURL更新时,同步更新ref的current值
useEffect(() => {
  avatarURLRef.current = avatarURL;
}, [avatarURL]);

function fillPageData() {
  fetch("http://localhost:3000/get-user-pfp", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      userID: "UID-2",
    }),
    credentials: "include",
  })
    .then((response) => response.blob())
    .then((data) => {
      const img = URL.createObjectURL(data);
      setAvatarURL(img);
    })
    .catch((err) => {
      console.log(err);
    });
}

useEffect(() => {
  const abortController = new AbortController();
  fillPageData();

  return () => {
    // 这里用ref的current值,就能拿到最新的头像URL
    console.log(avatarURLRef.current);
    URL.revokeObjectURL(avatarURLRef.current);
    abortController.abort();
  };
}, []);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:37:35