useRandomInterval


In JavaScript, we have two primitives for scheduling something in the future based on an amount of time:

  • setTimeout
  • setInterval

setTimeout is great for one-off events, and setInterval is great for things that happen on a fixed scheduleā€¦ but what if we want something to happen a bit more spontaneously?

This hook is great for animations and microinteractions. If you're generating particles for a confetti or firework effect, having a random delay between each particle can add a lot of life to the effect.

Usage

import { useCallback } from 'react'
import { useRandomInterval } from 'reactchemy'

export default function Component() {
   const callback = useCallback(() => {
      console.log('Interval callback executed')
   }, [])

   // Example of use: Starts the interval with a delay between 1000 and 2000 ms.
   useRandomInterval(callback, 1000, 2000)

   return (
      <div>
        <h2>useRandomInterval Demo</h2>
        <p>Check the Developer Tools console for the output history of the interval.</p>
      </div>
   )
}

Hook

import { useCallback, useEffect, useRef } from 'react'

// Utility helper for random number generation
const random = (min: number, max: number) => Math.floor(Math.random() * (max - min)) + min

export function useRandomInterval(callback: () => void, minDelay: number | undefined, maxDelay: number | undefined) {
   const timeoutId = useRef<number | null>(null)
   const savedCallback = useRef(callback)
   useEffect(() => {
      savedCallback.current = callback
   }, [callback])
   useEffect(() => {
      const isEnabled
      = typeof minDelay === 'number' && typeof maxDelay === 'number'
      if (isEnabled) {
         const handleTick = () => {
            const nextTickAt = random(minDelay, maxDelay)
            timeoutId.current = window.setTimeout(() => {
               savedCallback.current()
               handleTick()
            }, nextTickAt)
         }
         handleTick()
      }
      return () => window.clearTimeout(timeoutId.current!)
   }, [minDelay, maxDelay])
   const cancel = useCallback(() => {
      window.clearTimeout(timeoutId.current!)
   }, [])
   return cancel
}