Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 带挂钩的倒计时器_Reactjs_React Hooks - Fatal编程技术网

Reactjs 带挂钩的倒计时器

Reactjs 带挂钩的倒计时器,reactjs,react-hooks,Reactjs,React Hooks,我正在尝试自己实现倒计时,只是为了了解更多。我知道那里有图书馆,但我不想使用它。我的代码的问题是,我无法在“计时器”函数中获得更新状态,该函数在启动计时器函数中更新。我正在尝试实现计时器,该计时器将具有启动、停止和恢复触发器,并且可以手动触发。由使用倒计时组件的其他组件执行 import React, { useState } from 'react'; const Countdown = ({ countDownTimerOpt }) => { const [getObj, set

我正在尝试自己实现倒计时,只是为了了解更多。我知道那里有图书馆,但我不想使用它。我的代码的问题是,我无法在“计时器”函数中获得更新状态,该函数在启动计时器函数中更新。我正在尝试实现计时器,该计时器将具有启动、停止和恢复触发器,并且可以手动触发。由使用倒计时组件的其他组件执行

import React, { useState } from 'react';

const Countdown = ({ countDownTimerOpt }) => {
  const [getObj, setObj] = useState({
    formatTimer: null,
    countDownTimer: 0,
    intervalObj: null,
  });

  const { formatTimer, countDownTimer, intervalObj } = getObj;

  if (countDownTimerOpt > 0 && intervalObj === null) {
    startTimer();
  }

  function startTimer() {
    const x = setInterval(() => {
      timer();
    }, 1000);

    setObj((prev) => ({
      ...prev,
      countDownTimer: countDownTimerOpt,
      intervalObj: x,
    }));
  }

  function timer() {
    var days = Math.floor(countDownTimer / 24 / 60 / 60);
    var hoursLeft = Math.floor(countDownTimer - days * 86400);
    var hours = Math.floor(hoursLeft / 3600);
    var minutesLeft = Math.floor(hoursLeft - hours * 3600);
    var minutes = Math.floor(minutesLeft / 60);
    var remainingSeconds = countDownTimer % 60;

    const formatTimer1 =
      pad(days) +
      ':' +
      pad(hours) +
      ':' +
      pad(minutes) +
      ':' +
      pad(remainingSeconds);

    if (countDownTimer === 0) {
      clearInterval(intervalObj);
    } else {
      setObj((prev) => ({
        ...prev,
        formatTimer: formatTimer1,
        countDownTimer: prev['countDownTimer'] - 1,
      }));
    }
  }
  function pad(n) {
    return n < 10 ? '0' + n : n;
  }

  return <div>{formatTimer ? formatTimer : Math.random()}</div>;
};
export default Countdown;
import React,{useState}来自“React”;
常量倒计时=({countDownTimerOpt})=>{
常量[getObj,setObj]=useState({
formatTimer:null,
倒计时:0,
intervalObj:null,
});
常量{formatTimer,倒计时,intervalObj}=getObj;
如果(倒计时ROPT>0&&intervalObj==null){
startTimer();
}
函数startTimer(){
常数x=setInterval(()=>{
定时器();
}, 1000);
setObj((上一个)=>({
…上一页,
倒计时:倒计时ROPT,
时间间隔j:x,
}));
}
函数计时器(){
变量天数=数学下限(倒计时/24/60/60);
var Hourslft=数学楼层(倒计时-天数*86400);
var小时=数学楼层(左小时/3600);
var minutesLeft=数学楼层(小时数-小时数*3600);
var分钟=数学楼层(左分钟/60);
var remainingSeconds=倒计时%60;
常量格式化计时器1=
pad(天)+
':' +
pad(小时)+
':' +
pad(分钟)+
':' +
pad(剩余秒数);
如果(倒计时===0){
clearInterval(intervalObj);
}否则{
setObj((上一个)=>({
…上一页,
formatTimer:formatTimer1,
倒计时:上一个['countDownTimer']-1,
}));
}
}
功能板(n){
返回n<10?'0'+n:n;
}
返回{formatTimer?formatTimer:Math.random()};
};
出口默认倒计时;
import React,{useState,useffect}来自“React”;
从“../../components/countdown Timer/countdown.component”导入计时器;
常数训练=()=>{
常量[getValue,setValue]=useState(0);
useffect(()=>{
常数x=setTimeout(()=>{
log('setTimeout');
设定值(10000);
}, 5000);
返回()=>clearInterval(x);
}, []);
返回;

不希望在培训页面中使用任何设置的间隔,因为倒计时组件也将在考试页面中使用

通常与挂钩一起使用,我会将您的功能组合到自定义挂钩中,并在不同的位置使用它

const useTimer = (startTime) => {
    const [time, setTime] = useState(startTime)
    const [intervalID, setIntervalID] = useState(null)
    const hasTimerEnded = time <= 0
    const isTimerRunning = intervalID != null

    const update = () => {
        setTime(time => time - 1)
    }
    const startTimer = () => {
        if (!hasTimerEnded && !isTimerRunning) {
            setIntervalID(setInterval(update, 1000))
        }
    }
    const stopTimer = () => {
        clearInterval(intervalID)
        setIntervalID(null)
    }
    // clear interval when the timer ends
    useEffect(() => {
        if (hasTimerEnded) {
            clearInterval(intervalID)
            setIntervalID(null)
        }
    }, [hasTimerEnded])
    // clear interval when component unmounts
    useEffect(() => () => {
        clearInterval(intervalID)
    }, [])
    return {
        time,
        startTimer,
        stopTimer,
    }
}
const useTimer=(startTime)=>{
常量[时间,设置时间]=使用状态(开始时间)
常量[intervalID,setIntervalID]=useState(null)
常数=时间{
设置时间(时间=>time-1)
}
常数startTimer=()=>{
如果(!HastinmerEnded&!isTimerRunning){
setIntervalID(setInterval(更新,1000))
}
}
常量停止计时器=()=>{
clearInterval(有效期间)
setIntervalID(空)
}
//计时器结束时清除间隔
useffect(()=>{
如果(结束){
clearInterval(有效期间)
setIntervalID(空)
}
},[Hastinmerend])
//组件卸载时清除间隔
使用效果(()=>()=>{
clearInterval(有效期间)
}, [])
返回{
时间
斯塔蒂默,
计时器,
}
}
当然,您可以添加重置功能或进行其他更改,但使用可能如下所示:

const Training = () => {
    const { time, startTimer, stopTimer } = useTimer(20)
    return <>
        <div>{time}</div>
        <button onClick={startTimer}>start</button>
        <button onClick={stopTimer}>stop</button>
    </>
}
const Training=()=>{
const{time,startTimer,stopTimer}=useTimer(20)
返回
{time}
开始
停止
}

您可以创建
useCountDown
钩子,如下所示(在Typescript中):

从'react'导入{useffect,useRef,useState};
导出常量使用倒计时:(
总数:个,
女士:号码,
)=>[编号,()=>void,()=>void,()=>void]=(
总数:个,
ms:数量=1000,
) => {
常量[计数器,设置倒计时]=使用状态(总计);
常量[startCountDown,setStartCountDown]=useState(false);
//存储创建的间隔
const intervalId=useRef();
常量开始:()=>void=()=>setStartCountDown(true);
常量暂停:()=>void=()=>setStartCountDown(false);
常量重置:()=>void=()=>{
clearInterval(intervalId.current);
设置开始倒计时(假);
设置倒计时(总计);
};
useffect(()=>{
intervalId.current=setInterval(()=>{
开始计数和计数器>0和设置计数(计数器=>计数器-1);
},ms);
//计数为零时清除间隔
如果(计数器===0)clearInterval(intervalId.current);
//卸载时清除间隔
return()=>clearInterval(intervalId.current);
},[startCountDown,计数器,毫秒];
返回[计数器、启动、暂停、复位];
};

用法演示:

你能解释一下为什么我在timer()中得到countDownTimer=0吗函数,即使状态发生变化,我认为这是因为一个名为的概念。
setInterval
在第一次渲染时被调用,并且
timer
是在组件内部创建的。因此
timer
首先可以访问
countDownTimer
。在下一次渲染时,状态将发生更改,并创建一个新的将创建可访问更改状态的
计时器
函数,但间隔仍将调用旧的
计时器
函数。好的,谢谢,我已根据我的要求在自定义挂钩中更新了您的代码我已返回setTime,然后在培训组件中删除startTimer onclick在培训组件中添加useEffect使网络调用使用的setTime而不是调用startTimer()此处setTime工作正常,但不是startTimer()
const Training = () => {
    const { time, startTimer, stopTimer } = useTimer(20)
    return <>
        <div>{time}</div>
        <button onClick={startTimer}>start</button>
        <button onClick={stopTimer}>stop</button>
    </>
}
import { useEffect, useRef, useState } from 'react';

export const useCountDown: (
  total: number,
  ms?: number,
) => [number, () => void, () => void, () => void] = (
  total: number,
  ms: number = 1000,
) => {
  const [counter, setCountDown] = useState(total);
  const [startCountDown, setStartCountDown] = useState(false);
  // Store the created interval
  const intervalId = useRef<number>();
  const start: () => void = () => setStartCountDown(true);
  const pause: () => void = () => setStartCountDown(false);
  const reset: () => void = () => {
    clearInterval(intervalId.current);
    setStartCountDown(false);
    setCountDown(total);
  };

  useEffect(() => {
    intervalId.current = setInterval(() => {
      startCountDown && counter > 0 && setCountDown(counter => counter - 1);
    }, ms);
    // Clear interval when count to zero
    if (counter === 0) clearInterval(intervalId.current);
    // Clear interval when unmount
    return () => clearInterval(intervalId.current);
  }, [startCountDown, counter, ms]);

  return [counter, start, pause, reset];
};