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

如何修复React中无法终止的递归func()函数?

React递归函数无法终止的闭包问题解决

你的问题核心是React闭包陷阱:
组件第一次渲染时,func函数被创建,它捕获了当时的sum(值为6)和array(值为[1,2,3])。后续通过setArray更新状态后,组件会重新渲染,但之前通过setTimeout排队的递归调用,执行的还是初始版本的func——这个旧函数完全看不到新的状态值,所以sum >=10的终止条件永远不会触发,导致递归无限执行。

解决方案1:用useRef跟踪最新状态

useRef的.current属性是可变的,不会被闭包捕获旧值,我们可以用它同步保存最新的sum和array:

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

function App() {
    const [array, setArray] = useState([1, 2, 3]);
    const [sum, setSum] = useState(0);
    // 定义ref保存最新状态
    const sumRef = useRef(sum);
    const arrayRef = useRef(array);

    useEffect(() => {
        const currentSum = array.reduce((acc, curr) => acc + curr, 0);
        setSum(currentSum);
        // 同步更新ref的current值
        sumRef.current = currentSum;
        arrayRef.current = array;
    }, [array]);

    const getRandNum = () => {
        const num = Math.floor(Math.random() * 5);
        setArray(prev => [...prev, num]);
    };

    const func = () => {
        // 用ref的最新值判断终止条件
        if (sumRef.current >= 10) return;

        setTimeout(() => {
            getRandNum();
            func();
        }, 500);
    };

    return (
        <>
            <div className="App">
                {/* 给map元素添加key避免React警告 */}
                {array.map((num, index) => (
                    <p key={index}>{num}</p>
                ))}
            </div>
            <button onClick={() => func()}>click</button>
            <p>sum: {sum}</p>
        </>
    );
}

解决方案2:用useCallback让函数依赖状态更新

通过useCallback把func的依赖设为sum,这样每次sum变化时,func都会重新创建,确保递归调用的是能拿到最新状态的版本:

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

function App() {
    const [array, setArray] = useState([1, 2, 3]);
    const [sum, setSum] = useState(0);

    useEffect(() => {
        setSum(array.reduce((acc, curr) => acc + curr, 0));
    }, [array]);

    const getRandNum = () => {
        const num = Math.floor(Math.random() * 5);
        setArray(prev => [...prev, num]);
    };

    // 用useCallback绑定sum为依赖
    const func = useCallback(() => {
        if (sum >= 10) return;

        const timer = setTimeout(() => {
            getRandNum();
            func();
        }, 500);

        // 清理定时器,避免内存泄漏
        return () => clearTimeout(timer);
    }, [sum]);

    return (
        <>
            <div className="App">
                {array.map((num, index) => (
                    <p key={index}>{num}</p>
                ))}
            </div>
            <button onClick={() => func()}>click</button>
            <p>sum: {sum}</p>
        </>
    );
}

补充说明

  • 方案1更适合跟踪多个状态的场景,方案2更聚焦于状态变化时更新函数,两种都能解决闭包问题。
  • 方案2中添加定时器清理函数,防止组件卸载后定时器继续执行造成内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:40:18