简体   繁体   English

useEffect 不会更新 state

[英]useEffect won't update state

I'm creating a simple tomato app and got stuck on the timer update logic.我正在创建一个简单的番茄应用程序并卡在计时器更新逻辑上。

I'm using a useEffect to update a timer, but for some reason after the one second, the timer stop decreasing.我正在使用useEffect来更新计时器,但由于某种原因,一秒钟后,计时器停止减少。 It seems it's not getting updated since the console log keep printing the initial values.它似乎没有得到更新,因为控制台日志不断打印初始值。

here's a testable stackbliz这是一个可测试的stackbliz

here's the full component:这是完整的组件:

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;
    

That's the typical stale state issue, timer inside the interval is stale since you are not placing it into the effect deps.这是典型的陈旧 state 问题,间隔内的timer是陈旧的,因为您没有将它放入效果部门。 Just change timer.active to timer in the deps array.只需将 deps 数组中的timer.active更改为timer即可。

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

If you want to know more about this specific issue: https://overreacted.io/making-setinterval-declarative-with-react-hooks/如果您想了解有关此特定问题的更多信息: https://overreacted.io/making-setinterval-declarative-with-react-hooks/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM