1

I want to update the data on my page depending on whether it is active in the browser or not. This works simply with an if query against document.hasFocus().

But what I now also want is that when the focus goes back to the page an update happens immediately or at least within 5sec and then again normally after 30sec.

My thought now was to change the interval time within the interval. However, I have not been able to do this.

  useEffect(() => {
    const statusRefreshInterval = setInterval(() => {
      if (document.hasFocus()) {
        console.log("hasFocus:", new Date().toLocaleTimeString());
        setInterval(statusRefreshInterval, 30000);
      } else {
        console.log("hasNoFocus:", new Date().toLocaleTimeString());
        setInterval(statusRefreshInterval, 5000);
      }
    }, 30000);
    return () => clearInterval(statusRefreshInterval);
  }, []);

EDIT: In this way, I have fulfilled all my requirements:

  var lastUpdate = useRef();  // needed to use variables inside useEffect
  const allowedUpdatesAfterSec = 300;
  useEffect(() => {
    // initial update
    lastUpdate.current = Date.now() / 1000;

    // update when website gets focus again
    window.addEventListener("focus", function () {
      if (Date.now() / 1000 - lastUpdate.current > allowedUpdatesAfterSec) {
        console.log("EventListenerUpdate:", new Date().toLocaleTimeString());
        lastUpdate.current = Date.now() / 1000;
      }
    });

    // update every allowedUpdatesAfterSec when website is active
    const updateRefreshInterval = setInterval(() => {
      if (Date.now() / 1000 - lastUpdate.current > allowedUpdatesAfterSec) {
        if (document.hasFocus()) {
          console.log("IntervalUpdate:", new Date().toLocaleTimeString());
          lastUpdate.current = Date.now() / 1000;
        }
      }
    }, 60000);
    return () => clearInterval(updateRefreshInterval);
  }, []);

I will consider switching to setTimeout instead of setInterval.

edass
  • 45
  • 7
  • Do you mean to change the interval "within" the interval period or before the next interval with a new value? You have hard coded values for each but perhaps a parameter is what you desire/need to have? https://stackoverflow.com/q/109086/125981 – Mark Schultheiss May 21 '21 at 16:53
  • "focus goes back to the page" a focus event does not bubble so perhaps you need to add an event handler for that event then handle that https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event – Mark Schultheiss May 21 '21 at 16:59
  • I’d consider using setTimeout rather than run the risk of piling up setIntervals - just to be on the safe side. – A Haworth May 21 '21 at 17:03

2 Answers2

2

You may call setTimeout recursively from setTimeout callback with a dynamic time e.g. -

let timeToCall = 1000
let seconds = Date.now()/1000
function dynamicTimeoutFunction() {
  console.log(`I was called after ${Date.now()/1000 - seconds} seconds`)
  timeToCall += 1000
  setTimeout(dynamicTimeoutFunction, timeToCall)
}

setTimeout(dynamicTimeoutFunction, timeToCall)
Pawan Sharma
  • 1,842
  • 1
  • 14
  • 18
0

You are just creating more intervals this way, since you are calling setInterval from within the setInterval. I would just change setInterval to setTimeout.

  useEffect(() => {
    const statusRefreshInterval = setTimeout(() => {
      if (document.hasFocus()) {
        console.log("hasFocus:", new Date().toLocaleTimeString());
        setTimeout(statusRefreshInterval, 30000);
      } else {
        console.log("hasNoFocus:", new Date().toLocaleTimeString());
        setTimeout(statusRefreshInterval, 5000);
      }
    }, 30000);
    return () => clearTimeout(statusRefreshInterval);
  }, []);

This way you get to set a new time for the next call.