Je vous en ai parlé dans mon dernier article J’abandonne VueJS en faveur de React .
Et je m’amuse donc à réécrire des petites notions que j’utilisais à foison côté Vue.
Comme cette abstraction permettant de lister des éléments, et d’effectuer une recherche asynchrone dessus. Très utile dans le cas de combobox avec un très large choix d’items, ou pour une recherche sur un service tiers.
Je l’ai appelé useAsyncSearch()
Et voici en gros comment il fonctionne:
export function Main () {
const {
fetching,
refresh,
setSearch,
data
} = useAsyncSearch({
async fetch ({ search }) {
// => Return whatever items you want
// asynchronously, so you can even
// hit a server here
return ["blue", "green", "red"].filter(item => {
if (!search) {
return true
}
return item.includes(search)
})
}
})
// The rest of your component goes here.
}
Comme vous le voyez, ce hook vous met à disposition 4 éléments:
fetching
une variable qui vous aidera à afficher si une recherche est en cours
refresh
, une méthode permettant de manuellement refaire une recherche
setSearch
, une méthode permettant de mettre à jour la valeur de la recherche
data
, le retour de votre fonction fetch
passée en paramètre.
Avec ces 4 éléments, vous pouvez ensuite construire un petit composant sympa:
<div>
<input onInput={(e) => setSearch(e.target.value)} />
{fetching && "Fetching..."}
{data && (
<ul>
{data.map(item => <li>{item}</li>)}
</ul>
)}
<button onClick={refresh} disabled={fetching}>Actualiser</button>
</div>
Le hook en lui-même embarque quelques petites fonctionnalités comme:
data
est automatiquement typée en fonction de ce que vous retournez dans la fonction fetch
que vous passez en paramètre. Cela vous permet d’écrire sereinement le reste de la logique de votre application. On réalise ça grâce en utilisant un composant générique.
import { useEffect, useRef, useState } from "react";
export type AsyncSearchProps<T> = {
fetch: ({ search }: { search: string }) => Promise<T>;
};
export function useAsyncSearch<T>(props: AsyncSearchProps<T>) {
// We setup our state, refs are used for internal purposes
// while states are used for rendering purposes.
const [fetching, setFetching] = useState(false);
const [resultState, setResultState] = useState<T | null>(null);
const search = useRef("");
const fetchCount = useRef(0);
// The refresh function that uses
// a simple count system to prevent
// flash and flaky results
const refresh = async () => {
fetchCount.current++;
setFetching(true);
const currentCount = fetchCount.current;
try {
const result = await props.fetch({
search: search.current,
});
if (currentCount === fetchCount.current) {
setResultState(result);
setFetching(false);
}
} catch (error) {
console.error(error);
if (currentCount === fetchCount.current) {
setFetching(false);
}
}
};
// We refresh the data on start
useEffect(() => {
refresh();
}, []);
// We return the tools
return {
fetching,
refresh,
setSearch: (searchValue: string) => {
search.current = searchValue;
refresh();
},
data: resultState,
};
}