descriptive text Hammerbot
descriptive text
typescript
react

Savez-vous planter des hooks?

Le principe

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.
} 
typescript

Comme vous le voyez, ce hook vous met à disposition 4 éléments:

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>
typescript

Comment ça fonctionne?

Le hook en lui-même embarque quelques petites fonctionnalités comme:

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,
  };
}
typescript

Chargement des commentaires