swr icon indicating copy to clipboard operation
swr copied to clipboard

Refresh interval but only when idle

Open shuding opened this issue 4 years ago • 1 comments
trafficstars

Discussed in https://github.com/vercel/swr/discussions/1558

Originally posted by hazae41 October 14, 2021 I would like a page that displays some feed, and refresh it automatically. However, it should not refresh while the user is active on the page, because it would be disturbing.

Is there a way to have a refresh interval, but only if the user is NOT doing anything e.g. scrolling the page, dragging mouse, typing something, etc.?

I could use the new Idle Detection API or listen for mouse and scroll events, but isn't there already a such feature in SWR?

shuding avatar Oct 15 '21 11:10 shuding

I did this workaround, it works fine but I think it can be improved performance-wise

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

export function useIdle(delay: number) {
  const [idle, setIdle] = useState(false)
  const timeout = useRef<NodeJS.Timeout>()

  const onActive = useCallback(() => {
    setIdle(false)
    if (timeout.current)
      clearTimeout(timeout.current)
    timeout.current = setTimeout(() => {
      setIdle(true)
    }, delay)
  }, [delay])

  useEffect(() => {
    onActive()

    addEventListener("mousemove", onActive)
    addEventListener("mousedown", onActive)
    addEventListener("touchmove", onActive)
    addEventListener("touchstart", onActive)
    addEventListener("focus", onActive)
    addEventListener("click", onActive)

    return () => {
      if (timeout.current)
        clearTimeout(timeout.current)
      removeEventListener("mousemove", onActive)
      removeEventListener("mousedown", onActive)
      removeEventListener("touchmove", onActive)
      removeEventListener("touchstart", onActive)
      removeEventListener("focus", onActive)
      removeEventListener("click", onActive)
    }
  }, [onActive])

  return idle
}

For example, refresh every 5 seconds if the user is inactive for more than 10 seconds

const refreshInterval = useIdle(10000) ? 5000 : undefined 
const data = useSWRInfinite<Data>(getKey, { refreshInterval })

hazae41 avatar Oct 16 '21 16:10 hazae41