function component 关于定时器的一个坑 2020-01-10

最近在慢慢由class component转用function component代替,写法清爽了很多,不过带来的问题也有点多。。。

问题

首先让我们使用function component实现一个倒计时功能

import React, {useEffect, useState} from 'react';
import { Button } from 'antd-mobile';

let ClearTimer = null;

const Test = ()=>{

    const [seconds, setSeconds] = useState(10);

    useEffect(()=>{
        ClearTimer = setInterval(countDown, 1000);

        return clearUp;
    }, []);

    const clearUp = ()=>{
        clearInterval(ClearTimer);
    };

    const countDown = ()=>{
        let num = seconds - 1;
        if(num < 1){
            clearInterval(ClearTimer);
        }
        setSeconds(num);
    };

    return (
        <div>
            <Button>剩余时间{seconds}s</Button>
        </div>
    )
};

export default Test;

代码很简单,利用setInterval逐秒递减。实际跑起来后,会发现 根本没有递减!!!一直卡在9s不动。

原因

究其原因,还是seconds取值的问题。countDown是个闭包传入setInterval,其中seconds的值,在传入的时候就已经决定了,不会再取当前值,所以每次执行countDown函数seconds的值都是10。

解决办法

1. setState传入函数参数

更改countDown的写法,setSeconds不直接传入数值,而是传入一个callback

const countDown = ()=>{
        setSeconds((pre)=>{
            console.log('pre = ', pre);
            let num = pre - 1;
            if(num <= 0){
                clearInterval(ClearTimer);
            }

            return num;
        });
    };

callback的参数,为当前的seconds值,而不再是闭包里的值,此时计算结果已经是正确了。

2. 利用useEffectsetTimer实现(不推荐,容易影响其他逻辑)

首先修改useEffect,让其依赖seconds进行刷新,setInterval替换成setTimeout

useEffect(()=>{
        ClearTimer = setTimeout(countDown, 1000);

        return clearUp;
    }, [seconds]);

再简单改下countDown,只要调用setSeconds就行

const countDown = ()=>{
        seconds > 0 && setSeconds(seconds - 1);
    };

更改后,每次setSeconds都会引起useEffect函数的刷新动作,从而重新生成一个新Timer,保证了闭包内seconds的值始终是最新的

总结

由闭包引起的变量值没更新的问题,在class component中比较少见,而在function component中一不注意就会踏入陷阱。在需要根据当前state值计算下一个state时,尽量传入setState的函数参数,使用其参数state来进行计算,避免受其他环境的影响,比如闭包、别处的引用修改。
注意:使用useReducer虽能解决数值更新的问题,却也无法解决闭包值的问题。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容