为了账号安全,请及时绑定邮箱和手机立即绑定

setInterval 刷新应用而不是更新状态

setInterval 刷新应用而不是更新状态

慕莱坞森 2022-05-26 11:09:21
我正在用 编写倒计时Expo。我正在使用functional components,所以我state是通过 React 的useState钩子处理的。let [state, setState] = useState({    secondsLeft: 25,    started: false,});如果我按 aButton它确实会触发此功能:let onPressHandler = (): void => {    if(!state.started) {        setState({...state, started: true});        setInterval(()=> {            setState({...state, secondsLeft: state.secondsLeft - 1});            console.log(state.secondsLeft);        }, 1000);    }}问题是每 1000 毫秒 Expo 刷新应用程序而不是更新状态。
查看完整描述

1 回答

?
眼眸繁星

TA贡献1873条经验 获得超9个赞

它更新状态,但它使用陈旧状态来执行此操作。一旦间隔开始,回调中的state变量将永远不会改变。setInterval


相反,请使用状态更新函数的 setter 形式,因此您始终使用当时的状态:


let onPressHandler = (): void => {

    if(!state.started) {

        setState({...state, started: true});

        setInterval(()=> {

            setState(currentState => {

                const newState = {...currentState, secondsLeft: currentState.secondsLeft - 1};

                console.log(newState.secondsLeft);

                return newState;

            });

        }, 1000);

    }

};

没有以下内容会更简洁console.log:


let onPressHandler = (): void => {

    if(!state.started) {

        setState({...state, started: true});

        setInterval(()=> {

            setState(currentState => {...currentState, secondsLeft: currentState.secondsLeft - 1});

        }, 1000);

    }

};

单独说明:如果您有彼此独立更新的状态项,最佳实践是为它们使用单独的状态变量。此外,由于它们在您的函数中是常量,因此最好将它们声明为const. 像这样:


const [secondsLeft, setSecondsLeft] = useState(25);

const [started, setStarted] = useState(false);


// ...

let onPressHandler = (): void => {

    if(!started) {

        setStarted(true);

        setInterval(()=> {

            setSecondsLeft(seconds => seconds - 1);

        }, 1000);

    }

};

此外,由于您不能完全依赖setInterval精确,我建议存储您的停止时间(“现在”加上 25 秒)并重新计算每次还剩多少秒:


let onPressHandler = (): void => {

    const stopTime = Date.now() + (DURATION * 1000);

    setStarted(true);

    setSecondsLeft(DURATION);

    const timer = setInterval(()=> {

        const left = Math.round((stopTime - Date.now()) / 1000);

        if (left <= 0) {

            clearInterval(timer);

            setStarted(false);

        } else {

            setSecondsLeft(left);

        }

    }, 1000);

};

现场示例(带有停止逻辑):

const {useState} = React;


const Example = () => {

    const DURATION = 25; // seconds

    const [started, setStarted] = useState(false);

    const [secondsLeft, setSecondsLeft] = useState(0);


    if (started) {

        return <div>Seconds left: {secondsLeft}</div>;

    }


    let onPressHandler = ()/*: void*/ => {

        const stopTime = Date.now() + (DURATION * 1000);

        setStarted(true);

        setSecondsLeft(DURATION);

        const timer = setInterval(()=> {

            const left = Math.round((stopTime - Date.now()) / 1000);

            if (left <= 0) {

                clearInterval(timer);

                setStarted(false);

            } else {

                setSecondsLeft(left);

            }

        }, 1000);

    };

    return (

        <input

            type="button"

            onClick={onPressHandler}

            value="Start"

        />

    );

};


ReactDOM.render(<Example />, document.getElementById("root"));

<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>


查看完整回答
反对 回复 2022-05-26
  • 1 回答
  • 0 关注
  • 178 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信