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

为何useEffect添加curValue为依赖时每计时器tick触发清理,类组件无此问题

问题原因说明

1. 关于useEffect清理函数的执行时机

你对清理函数的认知存在偏差:useEffect的清理函数不只是在组件销毁时触发,只要Effect的依赖项发生变化,React就会先执行上一次Effect返回的清理函数,再执行本次的Effect逻辑。

2. 加入curValue后频繁触发清理的原因

你每次计数更新都会修改curValue状态,触发组件重渲染。如果把curValue加入依赖数组,React每次检测到curValue变化,就会重新执行Effect:先跑清理函数销毁上一次的定时器,再创建新的定时器,自然就会出现每次tick都触发清理的现象。

3. 为什么不需要把curValue加入依赖

教程要求把Effect用到的所有变量加入依赖,指的是在Effect回调内直接引用的、来自组件作用域的变量。你这段代码里更新状态用的是setCounter的函数式写法:

setCounter((current) => current + parseInt(props.increment))

这里的最新计数值current是React自动传入的,你根本没有在Effect里直接引用外部的curValue变量,所以curValue完全不属于这个Effect的依赖项,不需要加入数组。

补充优化建议

你当前代码的Effect依赖还缺了props.increment:因为你在Effect里直接引用了props.increment,如果后续父组件传入的增量发生变化,现有定时器里的增量还是旧值,建议补充到依赖数组里。
另外你清理函数里的日志打印componentWillUnmount是不准确的,这个日志大部分时候打印的是Effect更新前的清理操作,只有组件卸载时的那次才对应类组件的componentWillUnmount生命周期。


你给出的两个计数器实现代码(修正转义字符后):

// 每秒按props.increment指定的步长增加计数
function HookCounter(props) {
    const [curValue, setCounter] = useState(0);
    const [isOn, setIsOn] = useState(true);

    function toggleTimer() {
        setIsOn(!isOn);
    }

    useEffect(() => {
        let _timer = null;

        function _update(x,y) {
            setCounter((current) => current + parseInt(props.increment));
        }

        if(isOn) {
            _timer = setInterval(
                _update,
                1000
            );  
        } else {
            clearInterval(_timer);
        }
        
        return () => {
            console.log('Effect清理执行');
            clearInterval(_timer);
        };
    }, [isOn, props.increment]); 

    let buttonLabel = "Start";
    if(isOn) {
        buttonLabel = "Stop";
    }

    return <div>HookCounter {curValue}  
    <button onClick={toggleTimer}>{buttonLabel}</button></div>;
}


class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.incAmount = parseInt(props.increment);
        this.state = {curValue: 0, isOn: false};

        this.toggleTimer = this.toggleTimer.bind(this);
    }

    componentDidMount() {
        this.startTimer();
    }

    startTimer() {
        this.setState({isOn: true});
        this._timer = setInterval(
            () => this.tick(),
            1000
        );
    }

    stopTimer() {
        if(this.state.isOn) {
            clearInterval(this._timer);
        }
        this.setState({isOn: false});
    }

    tick() {
        this.setState((state, props) => 
        ({
            curValue: (this.incAmount + state.curValue)
        }));
    }
  
    componentWillUnmount() {
        console.log("componentWillUnmount");
        this.stopTimer();
    }

    toggleTimer(x, y) {
        if(this.state.isOn) {
            this.stopTimer();
        } else {
            this.startTimer();
        }
    }

    render() {
        let buttonLabel = "Start";
        if(this.state.isOn)
            buttonLabel = "Stop";

        return <div>{this.state.curValue} <button onClick={this.toggleTimer}>{buttonLabel}</button></div>;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 03:00:02