【问题标题】:Countdown timer in react-nativereact-native 中的倒数计时器
【发布时间】:2018-08-05 15:55:13
【问题描述】:

我想在 react-native 中加载屏幕时从 3 倒计时到 1。我用这样的 setTimeOut 尝试过,但没有用。我在这里做错了什么?我怎样才能做到这一点?加载屏幕后,我想以 1 秒的间隔显示 3 =-> 2 ==> 1。这是我的代码。

 constructor(props) {
        super(props);

        this.state = {
            timer: 3
        }
    }

    // componentDidMount 
    componentDidMount() {
        setTimeout(() => {
            this.setState({
                timer: --this.state.timer
            })
        }, 1000);
    }

【问题讨论】:

    标签: react-native


    【解决方案1】:

    在您的代码中,setTimeout 在 componentDidMount 中被调用,并且 ComponetDidMount 将在整个 组件生命周期 中被调用一次。因此,setTimeout 中的函数只会被调用一次。即在第一次渲染之后但在连续渲染之后,不会调用 componentDidMount。

    您的问题的解决方案可以是:

    constructor(props: Object) {
      super(props);
      this.state ={ timer: 3}
    }
    
    componentDidMount(){
      this.interval = setInterval(
        () => this.setState((prevState)=> ({ timer: prevState.timer - 1 })),
        1000
      );
    }
    
    componentDidUpdate(){
      if(this.state.timer === 1){ 
        clearInterval(this.interval);
      }
    }
    
    componentWillUnmount(){
     clearInterval(this.interval);
    }
    
    render() {
      return (
        <View style={{ flex: 1, justifyContent: 'center', }}>
          <Text> {this.state.timer} </Text>
        </View> 
      )
    }

    'setInterval' vs 'setTimeout'

    Advantage of using a function in setState instead of an object

    memory leak because of setInterval: 如果我们在调用 clearInterval 之前卸载组件,则会出现内存泄漏,因为我们启动时设置的间隔并且计时器没有停止。 React 提供 componentWillUnmount 生命周期方法,作为在卸载或删除组件时清除任何需要清除的机会。

    我创建了一个名为 useCountDownTimer 的自定义挂钩,以便在功能组件中使用此功能。如果有兴趣please check this GitHub gist link

    【讨论】:

    • 如果您要使用当前状态更新状态,最好使用:this.setState((prevState) =&gt; { return { timer: prevState.timer-- } })
    • @fv_dev 使用这种方法有什么好处吗?
    • @MohammedAshfaq 他们说使用this.state 无法可靠地获取当前状态,而使用prevState 则可以。我还没有被一个例子说服。
    【解决方案2】:

    使用 setIntervalHooks(使用 useEffect)版本更新为 countdown反应原生:

    const [timerCount, setTimer] = useState(60)
    
    useEffect(() => {
      let interval = setInterval(() => {
        setTimer(lastTimerCount => {
            lastTimerCount <= 1 && clearInterval(interval)
            return lastTimerCount - 1
        })
      }, 1000) //each count lasts for a second
      //cleanup the interval on complete
      return () => clearInterval(interval)
    }, []);
    

    使用状态变量timerCount为:&lt;Text&gt;{timerCount}&lt;/Text&gt;

    【讨论】:

    • 你能解释一下lastTimerCount &lt;= 1 &amp;&amp; clearInterval(interval)在做什么吗?
    • @Tee 这会在间隔保持在 1 秒以下并且可以这么说完成后清除。 clearInterval 是浏览器窗口或 NodeJS 中的内置函数,用于清除间隔,使其不再执行。同样,setTimeout 也有 clearTimeout。
    • @alexanderdavide 感谢您的回复。直觉上,我认为这就是正在发生的事情。只是表达式的语法有点出乎意料——我一直在寻找像if (lastTimerCount &lt;= 1) {clearInterval(interval)} 这样的逻辑......条件表达式和方法之间的&amp;&amp; 是我需要考虑的部分。
    【解决方案3】:

    挂钩版本。

    function CountDown() {
       const [count, setCount] = useState(3)
    
       useEffect(() => 
         let interval = setInterVal(() => {
            setCount(prev => {
               if(prev === 1) clearInterval(interval)
               return prev - 1
            })
         })
         // interval cleanup on component unmount
         return () => clearInterval(interval)
       ), [])
    
       return <Text>{count}</Text>
    }
    

    【讨论】:

      【解决方案4】:

      如果有人想在按下按钮时再次启动计时器,这将是 react-hooks 中的代码:

      let timer = () => {};
      
      const myTimer = () => {
          const [timeLeft, setTimeLeft] = useState(30);
      
          const startTimer = () => {
              timer = setTimeout(() => {
                  if(timeLeft <= 0){
                      clearTimeout(timer);
                      return false;
                  }
               setTimeLeft(timeLeft-1);
              }, 1000)
           }
      
           useEffect(() => {
               startTimer();
               return () => clearTimeout(timer);
           });    
      
          const start = () => {
              setTimeLeft(30);
              clearTimeout(timer);
              startTimer();
          }
      
          return (
             <View style={styles.container}>
                <Text style={styles.timer}>{timeLeft}</Text>
                <Button onPress={start} title="Press"/>
          </View>
      )}
      

      在这个例子中,我使用了一个 30 秒的计时器

      【讨论】:

        【解决方案5】:

        用法:

        时间戳属性必须以秒为单位

         const refTimer = useRef();
          
          const timerCallbackFunc = timerFlag => {
            // Setting timer flag to finished
            console.warn(
              'You can alert the user by letting him know that Timer is out.',
            );
          };
            
            
         <Timer
         ref={refTimer}
         timestamp={moment(item?.time_left).diff(moment(), 'seconds')}
         timerCallback={timerCallbackFunc}
         textStyle={styles.timerTextAHL}
         />
        

        Timer.js

        import React, {
          useState,
          useEffect,
          useRef,
          forwardRef,
          useImperativeHandle,
        } from 'react';
        import { Text, View } from 'react-native';
        
        const Timer = forwardRef((props, ref) => {
          // For Total seconds
          const [timeStamp, setTimeStamp] = useState(
            props.timestamp ? props.timestamp : 0,
          );
          // Delay Required
          const [delay, setDelay] = useState(props.delay ? props.delay : 1000);
        
          // Flag for informing parent component when timer is over
          const [sendOnce, setSendOnce] = useState(true);
        
          // Flag for final display time format
          const [finalDisplayTime, setFinalDisplayTime] = useState('');
        
          useInterval(() => {
            if (timeStamp > 0) {
              setTimeStamp(timeStamp - 1);
            } else if (sendOnce) {
              if (props.timerCallback) {
                props.timerCallback(true);
              } else {
                console.log('Please pass a callback function...');
              }
              setSendOnce(false);
            }
            setFinalDisplayTime(secondsToDhms(timeStamp));
          }, delay);
        
          function secondsToDhms(seconds) {
            seconds = Number(seconds);
            var d = Math.floor(seconds / (3600 * 24));
            var h = Math.floor((seconds % (3600 * 24)) / 3600);
            var m = Math.floor((seconds % 3600) / 60);
            var s = Math.floor(seconds % 60);
        
            var dDisplay = d > 0 ? d + 'd ' : '';
            var hDisplay = h > 0 ? h + 'h ' : '';
            var mDisplay = m > 0 ? m + 'm ' : '';
            var sDisplay = s > 0 ? s + 's ' : '';
            return dDisplay + hDisplay + mDisplay + sDisplay;
          }
        
          const refTimer = useRef();
          useImperativeHandle(ref, () => ({
            resetTimer: () => {
              // Clearing days, hours, minutes and seconds
              // Clearing Timestamp
              setTimeStamp(props.timestamp);
              setSendOnce(true);
            },
          }));
        
          return (
            <View ref={refTimer} style={props.containerStyle}>
              <Text style={props.textStyle}>{sendOnce ? finalDisplayTime : '0'}</Text>
            </View>
          );
        });
        
        function useInterval(callback, delay) {
          const savedCallback = useRef();
        
          // Remember the latest function.
          useEffect(() => {
            savedCallback.current = callback;
          }, [callback]);
        
          // Set up the interval.
          useEffect(() => {
            function tick() {
              savedCallback.current();
            }
            if (delay !== null) {
              const id = setInterval(tick, delay);
              return () => {
                clearInterval(id);
              };
            }
          }, [delay]);
        }
        
        export default Timer;
        

        【讨论】:

          【解决方案6】:

          权力守则 希望这条路很容易

            componentDidMount() {
                  this.CounterInterval()
            }
            CounterInterval = () => {
              this.interval = setInterval(
                () =>   this.setState({
                  timer: this.state.timer - 1
                }, () => {
                  if (this.state.timer === 0) {
                    clearInterval(this.interval);
                  }
                }),
                1000
              );
            }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-04-14
            • 1970-01-01
            • 2019-10-20
            • 2020-09-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多