Skip to content

Instantly share code, notes, and snippets.

@awran5
Forked from Danziger/interval.hook.ts
Created October 31, 2020 21:28
Show Gist options
  • Select an option

  • Save awran5/113359eb1f98d1469f7ebbcf04e54da1 to your computer and use it in GitHub Desktop.

Select an option

Save awran5/113359eb1f98d1469f7ebbcf04e54da1 to your computer and use it in GitHub Desktop.

Revisions

  1. @Danziger Danziger revised this gist Oct 13, 2020. 1 changed file with 49 additions and 0 deletions.
    49 changes: 49 additions & 0 deletions throttled-raf.hook.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,49 @@
    import React, { useEffect, useRef } from 'react';

    /**
    * Use setInterval with Hooks in a declarative way.
    *
    * @see https://stackoverflow.com/a/59274004/3723993
    * @see https://overreacted.io/making-setinterval-declarative-with-react-hooks/
    */
    export function useThrottledRAF(
    callback: React.EffectCallback,
    delay: number | null,
    ): [React.MutableRefObject<number | null>, React.MutableRefObject<number | null>] {
    const intervalRef = useRef<number | null>(null);
    const rafRef = useRef<number | null>(null);
    const callbackRef = useRef(callback);

    // Remember the latest callback:
    //
    // Without this, if you change the callback, when setInterval ticks again, it
    // will still call your old callback.
    //
    // If you add `callback` to useEffect's deps, it will work fine but the
    // interval will be reset.

    useEffect(() => {
    callbackRef.current = callback;
    }, [callback]);

    // Set up the interval:

    useEffect(() => {
    if (typeof delay === 'number') {
    intervalRef.current = window.setInterval(() => {
    rafRef.current = window.requestAnimationFrame(() => {
    callbackRef.current();
    });
    }, delay);

    // Clear interval and RAF if the components is unmounted or the delay changes:
    return () => {
    window.clearInterval(intervalRef.current || 0);
    window.cancelAnimationFrame(rafRef.current || 0);
    };
    }
    }, [delay]);

    // In case you want to manually clear the interval or RAF from the consuming component...:
    return [intervalRef, rafRef];
    }
  2. @Danziger Danziger revised this gist Oct 13, 2020. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion interval.hook.ts
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@ import React, { useEffect, useRef } from 'react';
    */
    export function useInterval(
    callback: React.EffectCallback,
    delay: number,
    delay: number | null,
    ): React.MutableRefObject<number | null> {
    const intervalRef = useRef<number | null>(null);
    const callbackRef = useRef(callback);
    2 changes: 1 addition & 1 deletion timeout.hook.ts
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@ import React, { useEffect, useRef } from 'react';
    */
    export function useTimeout(
    callback: React.EffectCallback,
    delay: number,
    delay: number | null,
    ): React.MutableRefObject<number | null> {
    const timeoutRef = useRef<number | null>(null);
    const callbackRef = useRef(callback);
  3. @Danziger Danziger revised this gist Oct 13, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions timeout.hook.ts
    Original file line number Diff line number Diff line change
    @@ -9,8 +9,8 @@ import React, { useEffect, useRef } from 'react';
    export function useTimeout(
    callback: React.EffectCallback,
    delay: number,
    ): React.MutableRefObject<number> {
    const timeoutRef = useRef<number>();
    ): React.MutableRefObject<number | null> {
    const timeoutRef = useRef<number | null>(null);
    const callbackRef = useRef(callback);

    // Remember the latest callback:
    @@ -32,7 +32,7 @@ export function useTimeout(
    timeoutRef.current = window.setTimeout(() => callbackRef.current(), delay);

    // Clear timeout if the components is unmounted or the delay changes:
    return () => window.clearTimeout(timeoutRef.current);
    return () => window.clearTimeout(timeoutRef.current || 0);
    }
    }, [delay]);

  4. @Danziger Danziger revised this gist Oct 13, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions interval.hook.ts
    Original file line number Diff line number Diff line change
    @@ -9,8 +9,8 @@ import React, { useEffect, useRef } from 'react';
    export function useInterval(
    callback: React.EffectCallback,
    delay: number,
    ): React.MutableRefObject<number> {
    const intervalRef = useRef<number>();
    ): React.MutableRefObject<number | null> {
    const intervalRef = useRef<number | null>(null);
    const callbackRef = useRef(callback);

    // Remember the latest callback:
    @@ -32,7 +32,7 @@ export function useInterval(
    intervalRef.current = window.setInterval(() => callbackRef.current(), delay);

    // Clear interval if the components is unmounted or the delay changes:
    return () => window.clearInterval(intervalRef.current);
    return () => window.clearInterval(intervalRef.current || 0);
    }
    }, [delay]);

  5. @Danziger Danziger created this gist Dec 10, 2019.
    41 changes: 41 additions & 0 deletions interval.hook.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    import React, { useEffect, useRef } from 'react';

    /**
    * Use setInterval with Hooks in a declarative way.
    *
    * @see https://stackoverflow.com/a/59274004/3723993
    * @see https://overreacted.io/making-setinterval-declarative-with-react-hooks/
    */
    export function useInterval(
    callback: React.EffectCallback,
    delay: number,
    ): React.MutableRefObject<number> {
    const intervalRef = useRef<number>();
    const callbackRef = useRef(callback);

    // Remember the latest callback:
    //
    // Without this, if you change the callback, when setInterval ticks again, it
    // will still call your old callback.
    //
    // If you add `callback` to useEffect's deps, it will work fine but the
    // interval will be reset.

    useEffect(() => {
    callbackRef.current = callback;
    }, [callback]);

    // Set up the interval:

    useEffect(() => {
    if (typeof delay === 'number') {
    intervalRef.current = window.setInterval(() => callbackRef.current(), delay);

    // Clear interval if the components is unmounted or the delay changes:
    return () => window.clearInterval(intervalRef.current);
    }
    }, [delay]);

    // In case you want to manually clear the interval from the consuming component...:
    return intervalRef;
    }
    47 changes: 47 additions & 0 deletions throttled-callback.hook.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,47 @@
    import { useCallback, useEffect, useRef } from 'react';

    export function useThrottledCallback<A extends any[]>(
    callback: (...args: A) => void,
    delay: number,
    deps?: readonly any[],
    ): (...args: A) => void {
    const timeoutRef = useRef<number>();
    const callbackRef = useRef(callback);
    const lastCalledRef = useRef(0);

    // Remember the latest callback:
    //
    // Without this, if you change the callback, when setTimeout kicks in, it
    // will still call your old callback.
    //
    // If you add `callback` to useCallback's deps, it will also update, but it
    // might be called twice if the timeout had already been set.

    useEffect(() => {
    callbackRef.current = callback;
    }, [callback]);

    // Clear timeout if the components is unmounted or the delay changes:
    useEffect(() => window.clearTimeout(timeoutRef.current), [delay]);

    return useCallback((...args: A) => {
    // Clear previous timer:
    window.clearTimeout(timeoutRef.current);

    function invoke() {
    callbackRef.current(...args);
    lastCalledRef.current = Date.now();
    }

    // Calculate elapsed time:
    const elapsed = Date.now() - lastCalledRef.current;

    if (elapsed >= delay) {
    // If already waited enough, call callback:
    invoke();
    } else {
    // Otherwise, we need to wait a bit more:
    timeoutRef.current = window.setTimeout(invoke, delay - elapsed);
    }
    }, deps);
    }
    41 changes: 41 additions & 0 deletions timeout.hook.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    import React, { useEffect, useRef } from 'react';

    /**
    * Use setTimeout with Hooks in a declarative way.
    *
    * @see https://stackoverflow.com/a/59274757/3723993
    * @see https://overreacted.io/making-setinterval-declarative-with-react-hooks/
    */
    export function useTimeout(
    callback: React.EffectCallback,
    delay: number,
    ): React.MutableRefObject<number> {
    const timeoutRef = useRef<number>();
    const callbackRef = useRef(callback);

    // Remember the latest callback:
    //
    // Without this, if you change the callback, when setTimeout kicks in, it
    // will still call your old callback.
    //
    // If you add `callback` to useEffect's deps, it will work fine but the
    // timeout will be reset.

    useEffect(() => {
    callbackRef.current = callback;
    }, [callback]);

    // Set up the timeout:

    useEffect(() => {
    if (typeof delay === 'number') {
    timeoutRef.current = window.setTimeout(() => callbackRef.current(), delay);

    // Clear timeout if the components is unmounted or the delay changes:
    return () => window.clearTimeout(timeoutRef.current);
    }
    }, [delay]);

    // In case you want to manually clear the timeout from the consuming component...:
    return timeoutRef;
    }