代码之家  ›  专栏  ›  技术社区  ›  Robdll

useEffect不会更新状态

  •  0
  • Robdll  · 技术社区  · 2 年前

    我正在创建一个简单的番茄应用程序,但被定时器更新逻辑卡住了。

    我正在使用 useEffect 更新计时器,但由于某种原因,在一秒钟后,计时器停止减少。由于控制台日志一直在打印初始值,所以它似乎没有得到更新。

    这是一个可测试的 stackbliz

    以下是完整的组件:

    import './Tomato.css';
    import { useState, useEffect } from 'react';
    import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
    import { faPlay, faPause, faArrowsRotate, faArrowDown, faArrowUp } from '@fortawesome/free-solid-svg-icons'
    
    function Tomato() {
        
        const [settings, setSettings] = useState({session: 25, break: 5});
        const [timer, setTimer] = useState({minutes: '25', seconds: '00', active: false, isBreak: false});
        
    
        useEffect(() => {
            let timerInterval;
    
            if(timer.active) {
                timerInterval = setInterval(() => {
                    let seconds = parseInt(timer.seconds);
                    let minutes = parseInt(timer.minutes);
                    console.log(seconds, ':',minutes)
                    if(seconds === 0 && minutes === 0) {
                    //  setTimer( {
                    //      minutes: settings.isBreak ? settings.session : settings.break, 
                    //      seconds: '00', 
                    //      active: true, 
                    //      isBreak: !settings.isBreak
                    //  })
                    //  // TODO BIP
                    } else if(seconds === 0) {
                        const newTimer = {
                            ...timer,
                            minutes: timer.minutes-1,
                            seconds: '59',
                        }
                        setTimer( newTimer)
                    } else {
                        console.log('in')
                        seconds = seconds - 1;
                        // this is not working as expected
                        setTimer( {
                            ...timer,
                            seconds: seconds < 10 ? '0'+seconds : seconds+''
                        })
                    }
                }, 1000);
            } else {
                clearInterval(timerInterval);
            }
    
            return function cleanup() {
                clearInterval(timerInterval);
            };
        }, [timer.active]);
    
    
        const toggleTimer = (value) => {
            setTimer({ ...timer, active: value})
        }
    
        const refreshTimer = () => {
            setTimer({ 
                minutes: settings.session,
                seconds: '00', 
                active: false,
                isBreak: false
            })
        }
    
        const editSettings =  (type, val) => {
            if(timer.active) return
            const newSettings = { ...settings }
            newSettings[type] +=val;
            if(newSettings[type] > 60) {
                newSettings[type] = 60
            }
            if(newSettings[type] < 1) {
                newSettings[type] = 1
            }
            setSettings(newSettings)
        }
    
        return (
            <div className="Tomato">
                
                <h2 className="title">Session</h2>
                <div className="session-container">
                    <div className='session-value'>{timer.minutes}:{timer.seconds}</div>
                    <div className='controls-container'>
                        <span className='icon-container'>
                            <FontAwesomeIcon onClick={()=>{ toggleTimer(true)}} icon={faPlay} />
                        </span>
                        <span className='icon-container'>
                            <FontAwesomeIcon onClick={()=>{ toggleTimer(false)}} icon={faPause} />
                        </span>
                        <span className='icon-container'>
                            <FontAwesomeIcon onClick={()=>{ refreshTimer()}} icon={faArrowsRotate} />
                        </span>
                    </div>
                </div>
    
                <h2 className="title">Session Length</h2>
                <div className="setting-container">
                    <span className='icon-container' onClick={()=> editSettings('session', -1)}>
                        <FontAwesomeIcon icon={faArrowDown} />
                    </span>
                    <div className="setting-value">{settings.session}</div>
                    <span className='icon-container' onClick={()=> editSettings('session', 1)}>
                        <FontAwesomeIcon icon={faArrowUp} />
                    </span>
                </div>
                
                <h2 className="title">Break Length</h2>
                <div className="setting-container">
                    <span className='icon-container' onClick={()=> editSettings('break', -1)}>
                        <FontAwesomeIcon icon={faArrowDown} />
                    </span>
                    <div className="setting-value">{settings.break}</div>
                    <span className='icon-container' onClick={()=> editSettings('break', +1)}>
                        <FontAwesomeIcon icon={faArrowUp} />
                    </span>
                </div>
                
            </div>
            );
        }
        
        export default Tomato;
        
    
    1 回复  |  直到 2 年前
        1
  •  2
  •   Cesare Polonara    2 年前

    这是典型的陈腐状态问题, timer 间隔内是过时的,因为你没有把它放在效果deps中。只要改变 timer.active 定时器 在deps阵列中。

    ...
      return function cleanup() {
                clearInterval(timerInterval);
            };
        }, [timer]);
    

    如果您想了解有关此特定问题的更多信息: https://overreacted.io/making-setinterval-declarative-with-react-hooks/