Home > Mobile >  What is the type of useRef if I want to referenciate a callback?
What is the type of useRef if I want to referenciate a callback?

Time:04-12

I am following Dan Abramov's approach to creating a custom hook for a basic setInterval use case. However, I'm having a hard time typing it in typescript. Especially, what should be the type of useRef? Here is the code in js, would love a transcription to typescript if possible

import React, { useState, useEffect, useRef } from 'react';

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

In my code I would use it like this:

export interface DataProps<Type> {
  results: Type[];
  isLoading: boolean;
  LIMIT: number;
  offset: number;
}

export interface Pokemon {
  name: string;
  url: string;
}

const [data, setData] = useState<DataProps<Pokemon>>({
    results: [],
    isLoading: true,
    LIMIT: 20,
    offset: 0
  });

useInterval(
    async () => {
      try {
        const res = await fetch(
          `https://pokeapi.co/api/v2/pokemon?`  
            new URLSearchParams({
              limit: data.LIMIT   "",
              offset: data.offset   ""
            })
        );
        const { results } = await res.json();

        setData((prevValue) => ({
          ...prevValue,
          results: [...prevValue.results, ...results],
          isLoading: false,
          offset: prevValue.offset   prevValue.LIMIT
        }));
      } catch (err) {
        console.log("Fetch Error:", err);
      }
    },
    10000
  );

CodePudding user response:

You'd type your useRef to your callback type:

const savedCallback = useRef<typeof callback>();

If you're asking instead what your callback type should be, it would depend on what you want the function shape to be. Most common ones are () => Promise<void> | void for async or sync functions, or (...args: any) => Promise<void> | void for functions with arguments.

CodePudding user response:

I wrote a hook which allows you to call always the very latest instance of the callback:

import { useRef } from "react";
export const useLatestCallback = <
  T extends (...args: any[]) => any
>(
  callback: T
): T => {
  const cb = useRef({
    c: callback,
    run: (...args: Parameters<T>): ReturnType<T> => cb.c(...args),
  }).current;
  cb.c = callback;
  return cb.run as T;
};

This allows your code to just use the latest callback:

function useInterval<
  T extends (...args: any[]) => any
>(callback: T, delay: number | null) {
  const latestCallback = useLatestCallback(callback);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      latestCallback();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}
  • Related