簡體   English   中英

如何防止在 React js 中重新加載頁面的計時器?

[英]How to prevent the timer on reload the page in React js?

我試圖阻止計數器在頁面刷新時重新加載。 這是代碼:

import React, { useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import { timerEnd } from "./states-manager/timer-slice";

export default function Timer() {
  // const lol =localStorage.getItem('timer')

  const [countdown, setCountdown] = useState({
    expirityTime: 100,
    expired: false
  });

  const [timer, setTimer] = useState()
  const [focus, setFocus] = useState(true)
  const dispatch = useDispatch();
 
  // Stop Counter when tab is switched
  
useEffect(()=>{
  window.onblur = function () { 
    setFocus(false)
  }; 
  window.onfocus = function () { 
    setFocus(true)
    setTimer(countdown.expirityTime)

  }; 

})

  useEffect(() => {
    let time = countdown.expirityTime;
    if (time > 0 && (focus)) {
      const timerId = setTimeout(() => {
          setCountdown({ expirityTime:time- 1 }); 
      }, 1000);
      return () => clearTimeout(timerId);
    }
  });

  //Passing CountDown Vlaue to the Quiz.js through Redux Toolkit
  dispatch(timerEnd(timer));

  return (
    <div className="App">
      <div className="timer">
        Time Remaining: {Math.floor(countdown.expirityTime / 60)}:
        {countdown.expirityTime % 60}
      </div>
    </div>
  );
}

我可以將倒計時時間存儲在本地存儲中,但由於代碼,它也會重新啟動。 如果你能幫我解決這個問題,請告訴我。 如果您需要任何進一步的信息,請告訴我。

我實際上會重新考慮當前計時器的實現方式,以使其更加准確。 但是您可以before unload利用 window,並設置一個並行計時器,每隔一段時間更新本地存儲。 也就是說,您需要小心設置local storage過於頻繁,因為如果您保存大量對象/數據,尤其是在較小的設備上,它會極大地影響性能。 這是因為對localstorage的讀寫是同步操作,通常需要JSON.stringifyJSON.parse進行讀寫。 我的計時器實現如下。 關鍵要點是您需要:

  1. 定時器暫停和取消暫停時使用定時器數據更新本地存儲
  2. 使用開始時間和結束時間來跟蹤計時器,因為計數器不准確(React state 更新並非一直都是即時的)
  3. 以更新本地存儲的間隔運行 function。 間隔越低,就性能而言,它的成本越高。
import React, { useState, useEffect } from "react";
import { unstable_batchedUpdates } from "react-dom";
import {useDispatch} from 'react-redux'
function addMinutes(date, minutes) {
  return new Date(date.getTime() + minutes * 60000);
}
function addMillseconds(date, millseconds) {
  return new Date(date.getTime() + millseconds);
}
export default function Timer() {
  const currDate = new Date();
  const [timeInactiveStart, setTimeInactiveStart] = useState(currDate);
  const [inactiveTimerActive, setInactiveTimerActive] = useState(false);
  const [timeStart, setTimeStart] = useState(currDate);
  const [timeEnd, setTimeEnd] = useState(addMinutes(currDate, 10));
  const timeRemaining = timeEnd.getTime() - timeStart.getTime();
  const dispatch = useDispatch()
  // Stop Counter when tab is switched
  useEffect(() => {
    window.onblur = function () {
      //start parallel timer
      const timeStartDate = new Date();
      //we save here in case the user closes the page while away.
      //that way we still know how much time they have left
      const timerObj = {
        startTime: timeStartDate,
        endTime: timeEnd,
        remainingTime: timeEnd.getTime() - timeStartDate.getTime(),
        inactiveTimerActive: true,
      };
      localStorage.setItem("timerData".JSON.stringify(timerObj));
      setTimeInactiveStart(timeStartDate);
      //stop timer
      setInactiveTimerActive(true);
    };
    window.onfocus = function () {
      //end parallel timer
      const timeInactiveEnd = new Date();
      const timeElapsedInactive =
        timeInactiveEnd.getTime() - timeInactiveStart.getTime();
      //add time to intervals and now we can store them, so its not out of sync
      const newEndTime = addMillseconds(timeEnd, timeElapsedInactive);
      const newStartTime = addMillseconds(timeStart, timeElapsedInactive);
      const timerObj = {
        startTime: newStartTime.toString(),
        endTime: newEndTime.toString(),
        //we store this in case a user exists the page, we have a restarting point
        remainingTime: newEndTime.getTime() - newStartTime.getTime(),
        inactiveTimerActive: false,
      };
      unstable_batchedUpdates(() => {
        localStorage.setItem("timerData", JSON.stringify(timerObj));
        setTimeEnd(newEndTime);
        setTimeStart(newStartTime);
        //restart timer
        setInactiveTimerActive(false);
      });
    };
    window.onbeforeunload = function () {
      //by nature this wont always occur, so
      //if you need to keep the timer countdown with higher integrity,
      // consider updating to local storage every minute or 5 minutes, depending on
      // your use case. However, every second would be too frequent
      const timerObj = {
        startTime: timeStart.toString(),
        endTime: timeEnd.toString(),
        //we store this in case a user exists the page, we have a restarting point
        remainingTime: timeEnd.getTime() - timeStart.getTime(),
        inactiveTimerActive: inactiveTimerActive,
      };
      localStorage.setItem("timerData", JSON.stringify(timerObj));
    };
  });
  //set a timer for a custom interval.
   //  To create checkpoints. However, the lower the time between intervals,
  //the larger the performance hit, so don't update every second. In this example it updates every minute
  useEffect(() => {
    const updateStorage = setInterval(() => {
      const timerObj = {
        startTime: timeStart,
        endTime: timeEnd,
        inactiveTimerActive: inactiveTimerActive,
        remainingTime: timeEnd.getTime() - timeStart.getTime(),
      };
      localStorage.setItem("timerData", JSON.stringify(timerObj));
    }, 60000);
    return () => clearInterval(updateStorage);
  }, [timeEnd, timeStart, inactiveTimerActive]);

  useEffect(() => {
    const timer = setInterval(() => {
      //we increment if these are correct
      if (!inactiveTimerActive && timeStart.getTime() < timeEnd.getTime()) {
        setTimeStart(new Date());
      }
      //clear local storage if timer has ended
      else if (timeStart.getTime() > timeEnd.getTime())
        localStorage.removeItem("timerData");
    }, 1000);
    return () => clearInterval(timer);
  }, [inactiveTimerActive, timeStart, timeEnd]);

  //on mount we fetch from localstorage our timer values
  useEffect(() => {
    const timerData = localStorage.getItem("timerData");
    if (timerData) {
      const data = JSON.parse(timerData);
      const newDate = new Date();
      unstable_batchedUpdates(() => {
        setTimeStart(new Date());
        //add time remaining since timer was inactive when tab was closed
        setTimeEnd(addMillseconds(newDate, data.remainingTime));
      });
    }
  }, []);
  //here you can pass time remaining anywhere, etc.
  //Passing CountDown Vlaue to the Quiz.js through Redux Toolkit
  //dispatch(timerEnd(timer));
  return (
    <div className="App">
      <div className="timer">
        Time Remaining:{" "}
        {timeRemaining > 0 ? Math.floor(timeRemaining / 1000) : 0} seconds
      </div>
    </div>
  );
}

我做了一個反應鈎子,可以在客戶端用作計時器或倒計時計時器,請在 github 找到代碼和示例: PersistantTimer

除了 react 不需要依賴。

或者你可以找到下面的代碼:

import { useState, useEffect, useRef } from "react";

const initTimer = { //initial value of ref
    lastSavedElapsedTime: 0,
    elapsedTime:  0,
    intervalId: null as ReturnType<typeof setInterval> | null,
    start: 0,
    manuallyPaused: false
}
interface OPTIONS {
    updateFrequency?: number,
    maximumValue?:number,
    callback?:(()=>void)
    LocalStorageItemName?:string
}
const defaultOptions:OPTIONS = {

    updateFrequency: 1,
    maximumValue:0,
    callback:undefined,
    LocalStorageItemName:'Persistant_timer'
}
const usePersistantTimer = (
    pauseOnNoFocus: boolean = true, {
    updateFrequency = 1,
    maximumValue = 0,
    callback,
    LocalStorageItemName= 'Persistant_timer'}:OPTIONS = defaultOptions
): [number, () => void, () => void, () => void] => {
    const timer = useRef(initTimer)
    const cu = timer.current
    const getValueFromLocalStorage = () => {
        let v = parseInt(localStorage.getItem(LocalStorageItemName) || '0')
        if (isNaN(v) || v < 0) v = 0
        cu.lastSavedElapsedTime = v
        cu.elapsedTime = 0
        cu.start = new Date().getTime()
    }
    const [elapsedTime, setElapsedTime] = useState(cu.lastSavedElapsedTime)

    const PIN = (i: number) => { // set parameter to default 1 if the paramenter is not legal number.
        return (i > 1 && Number.isInteger(i)) ? i : 1
    }
    const updateFrequnce = PIN(updateFrequency)
    const start = () => {
        if (cu.manuallyPaused) cu.manuallyPaused = false
        if (!cu.intervalId) {
            getValueFromLocalStorage()
            cu.intervalId = setInterval(() => {
                cu.elapsedTime = new Date().getTime() - cu.start + cu.lastSavedElapsedTime
                //t preserve real elapsed time. 

                if (!(cu.elapsedTime % updateFrequnce)) setElapsedTime(cu.elapsedTime)

                if (maximumValue && cu.elapsedTime >= maximumValue * 1000) {
                    if (callback) callback()
                    cu.elapsedTime = 0
                    cu.manuallyPaused = true
                    pause()
                }
                localStorage.setItem(LocalStorageItemName, cu.elapsedTime.toString())
            }, 1000)
        }
    }
    const pause = () => {
        cu.lastSavedElapsedTime = cu.elapsedTime
        if (cu.intervalId) {
            clearInterval(cu.intervalId)
            cu.intervalId = null
        }
    }
    const manuallyPause = () => {
        cu.manuallyPaused = true
        pause()
    }
    const resetTimer = () => {
        cu.lastSavedElapsedTime = 0
        cu.elapsedTime = 0
        localStorage.setItem(LocalStorageItemName, "0")
        cu.start = new Date().getTime()
        setElapsedTime(0)
    }

    useEffect(() => {
        getValueFromLocalStorage()
        window.onblur = () => {
            if (pauseOnNoFocus) pause()
        }

        window.onfocus = () => {
            if (cu.manuallyPaused) return
            if (pauseOnNoFocus) start()
        }

        start()

        return () => {
            if (cu.intervalId) {
                clearInterval(cu.intervalId)
                cu.intervalId = null
            }
        }
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [pauseOnNoFocus, LocalStorageItemName, maximumValue, callback, updateFrequency])

    return [elapsedTime, start, manuallyPause, resetTimer]
}

export default usePersistantTimer

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM