【问题标题】:setInterval refreshes the app instead of updating the statesetInterval 刷新应用而不是更新状态
【发布时间】:2020-03-21 17:18:21
【问题描述】:

我正在使用Expo 编写倒计时代码。 我使用的是functional components,所以我的state 是通过React 的useState 钩子处理的。

let [state, setState] = useState({
    secondsLeft: 25,
    started: false,
});

如果我按下Button,它会触发此功能:

let onPressHandler = (): void => {
    if(!state.started) {
        setState({...state, started: true});
        setInterval(()=> {
            setState({...state, secondsLeft: state.secondsLeft - 1});
            console.log(state.secondsLeft);
        }, 1000);
    }
}

问题是每 1000 毫秒 Expo 刷新应用而不是更新状态。

你能帮帮我吗?

【问题讨论】:

    标签: javascript typescript react-native expo setinterval


    【解决方案1】:

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

    改为使用状态更新函数的 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>

    【讨论】:

    • 非常感谢您非常清晰详尽的解释!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-09
    • 2018-12-22
    • 1970-01-01
    • 1970-01-01
    • 2020-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多