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

React中useState变量在useEffect状态变更后调用函数不更新问题

问题根源:React闭包陷阱

你遇到的问题本质是闭包捕获了旧的状态值。组件首次渲染时,testIfDoneLoading函数捕获了当时的loading状态(true),当你把这个函数存入queuedFunction.current后,即使后续setLoading(false)更新了状态,调用缓存的函数时,它依然使用的是首次渲染时的loading值,导致再次进入暂存逻辑。


解决方案

方案一:用useRef同步状态,获取最新值

通过useRef同步loading状态,确保函数能拿到最新的状态值:

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

export default function App() {
  const [loading, setLoading] = useState(true);
  const loadingRef = useRef(loading); // 用ref同步最新loading状态
  const queuedFunction = useRef<(() => void) | undefined>(undefined);

  // 同步state和ref
  useEffect(() => {
    loadingRef.current = loading;
  }, [loading]);

  useEffect(() => {
    testIfDoneLoading();

    // 模拟异步加载
    setTimeout(() => {
      setLoading(false);
    }, 1000);
  }, []);

  useEffect(() => {
    if (!loading && queuedFunction.current) {
      queuedFunction.current();
      queuedFunction.current = undefined;
    }
  }, [loading]);

  function testIfDoneLoading() {
    // 读取ref中的最新loading值
    if (loadingRef.current) {
      console.log('Still Loading');
      queuedFunction.current = () => testIfDoneLoading();
    } else {
      console.log('Done Loading!');
    }
  }

  return <div>{loading ? 'Loading...' : 'Ready'}</div>;
}

方案二:直接缓存最终执行逻辑

如果testIfDoneLoading的最终逻辑简单,可以直接缓存目标操作,避免嵌套调用带来的闭包问题:

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

export default function App() {
  const [loading, setLoading] = useState(true);
  const queuedAction = useRef<(() => void) | undefined>(undefined);

  useEffect(() => {
    testIfDoneLoading();

    setTimeout(() => {
      setLoading(false);
    }, 1000);
  }, []);

  useEffect(() => {
    if (!loading && queuedAction.current) {
      queuedAction.current();
      queuedAction.current = undefined;
    }
  }, [loading]);

  function testIfDoneLoading() {
    if (loading) {
      console.log('Still Loading');
      // 直接缓存最终要执行的逻辑
      queuedAction.current = () => console.log('Done Loading!');
    } else {
      console.log('Done Loading!');
    }
  }

  return <div>{loading ? 'Loading...' : 'Ready'}</div>;
}

方案三:用useCallback确保函数始终最新

通过useCallback包裹testIfDoneLoading,让它在loading变化时更新,确保缓存的函数能拿到最新状态:

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

export default function App() {
  const [loading, setLoading] = useState(true);
  const queuedFunction = useRef<(() => void) | undefined>(undefined);

  // 用useCallback包裹,依赖loading变化时更新函数
  const testIfDoneLoading = useCallback(() => {
    if (loading) {
      console.log('Still Loading');
      queuedFunction.current = () => testIfDoneLoading();
    } else {
      console.log('Done Loading!');
    }
  }, [loading]);

  useEffect(() => {
    testIfDoneLoading();

    setTimeout(() => {
      setLoading(false);
    }, 1000);
  }, [testIfDoneLoading]); // 依赖更新后的函数

  useEffect(() => {
    if (!loading && queuedFunction.current) {
      queuedFunction.current();
      queuedFunction.current = undefined;
    }
  }, [loading]);

  return <div>{loading ? 'Loading...' : 'Ready'}</div>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 12:58:10