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]);
}