Comment j'organise mes projets Typescript (partie 2)

Comment j'organise mes projets Typescript (partie 2)
Alexandre P. dans Dev - mis à jour le 29-04-2025

Optimisez vos projets React/Next.js avec une architecture DRY, des hooks organisés et l'efficacité TypeScript. Gagnez en qualité et en productivité !

Cela fait maintenant 9 ans que j'utilise React, j'ai commencé à l'époque en class component et j'ai migré petit à petit vers le functional que je préfère de loin par sa simplicité.

Mais j'ai vu ce langage évoluer et devenir ce qu'il est aujourd'hui, en passant par des phases où il s'est complexifié puis de nouveau simplifié.

Aujourd'hui, on peut exploiter réellement la puissance de React lorsqu'il est couplé à Typescript.

Beaucoup de développeurs pensent que Typescript complique le développement, or, pour des développeurs très avancés, il simplifie tout.

  • La correction en temps réel des erreurs via tsc, lint
  • La robustesse des livrables tant on travaille en territoire connu
  • Une anticipation des comportements

Mes conseils d'Expert Next.js

Revenir à JS pour un développeur avancé, c'est comme si tu disais à quelqu'un qui a une voiture de repasser sur un cheval... De la perte de temps !

Typescript est un langage que j'ai adopté un peu plus tard, en 2018/2019 via des apps mobiles.

J'y suis revenu ensuite en 2021 définitivement sans aucun retour en arrière possible tellement cet outil est riche de possibilités.

Je vais vous partager aujourd'hui, comme suite de mon article sur l'organisation Typescript , des conseils qui font de moi un Expert React, Next.js avec plus de 20 projets déployés, des dizaines de millions d'ARR générés pour mes clients.

L'expérience du code c'est une chose, l'expérience de la production sur des projets qui ramènent de l'argent en est une autre. Si je me trompe dans mes choix, c'est un impact direct sur les revenus de mes clients, c'est pourquoi j'ai fait le choix d'outils robustes et de méthodologies rigoureuses.

La pratique du DRY

Qui n'a jamais travaillé sur un projet où l'on redéclare exactement les mêmes requêtes sur chaque pages pour récupérer les utilisateurs, ou encore pour récupérer les produits que ce soit sur une liste, une liste filtrée, une fiche produit, une liste des commandes avec détail produits, etc...

Il y a exactement les mêmes requêtes et on se répète encore et encore.

Pire, si on nous demande de modifier la moindre chose, il faut repasser sur absolument toutes les pages pour répéter la modification et il suffira d'un oubli pour commencer à créer des écarts de comportement et casser l'app. Aujourd'hui je vais vous proposer une approche totalement DRY compliant, qui a été testé sur de multiples projets et fait ses preuves.

L'organisation de requêtes en hook React

Alors bien sûr, il faut commencer par utiliser un outil de requête, j'utilise React-Query depuis quelques années déjà, mais je pense que tout autre outil comme SWR etc peut aussi faire l'affaire.

Assurez vous simplement que vous avez un système de caching pour tirer profit de tout cela. React-Query a une approche clé de cache qui vous permet de ne pas refaire un appel back si vous faites plusieurs fois le même appel pendant le rendu (ex: 2 composants qui ont besoin du même jeu de données).

Une fois que vous aurez fait tout le travail de base de mise en place avec le Provider de React-Query (que vous pouvez tout simplement mettre en place en suivant le guide ) nous allons nous pencher sur l'organisation de nos requêtes.

L'arborescence

Je l'ai déjà dit plus haut mais, une requête = un hook, on va se contenter de faire simple. Et pour une meilleure organisation des hooks, je crée une arborescence comme ceci:

/hooks
|
|__ /users
|    |__ useListUsers.tsx
|    |__ useGetUser.tsx
|    |__ useSaveUser.tsx
|    |__ useDeleteUser.tsx
|
|__ /products
|    |__ useListProducts.tsx
|    |__ useGetProduct.tsx
|    |__ useSaveProduct.tsx
|    |__ useDeleteProduct.tsx

Comme vous pouvez le voir j'effectue un découpage des requêtes par model afin de s'y retrouver plus facilement quand on va importer notre hook.

Le nommage des hooks

Comme vous pouvez le voir, j'ai une règle de nommage pour mes hooks. Cela me permet de savoir le type de résultat qu'est sensé renvoyer mon hook:

  • useList... : le hook renvoie un array
  • useGet... : le hook renvoie un objet
  • useSave... : est un hook de mutation qui passera de create à update en fonction d'un ID que je passerai en argument
  • useDelete... : le hook de mutation qui sert à faire des suppression

L'utilisation des hooks

Les développeurs React savent que les hooks s'utilisent en entrée de page, (de toute façon on ne peut pas l'utiliser après avoir commencer à faire de la logique comme des conditions etc...).

Cela signifie que la première chose que fait mon composant, c'est de déclencher les appels pour récupérer de la donnée.

Oui, votre composant n'a pas encore reçu la réponse, mais pas de panique, un useQuery sur React-Query permet toujours d'avoir un état de la requête.

const { data, isLoading } = useGetUser()

// Au 1er run du composant data est vide car il n'a pas encore reçu la réponse du backend
// on utilisera isLoading pour montrer cet état

return (
  <>
     {isLoading ? <Spinner /> : <UserComponent user={data} />} 
  </>
)

En ce qui concerne les mutations, donc les requêtes de type écriture: ajout, modification ou encore suppression, on va procéder différemment.

On sait déjà que tout ce qui est écriture est asynchrone dans une logique de page web.

Un utilisateur effectue toujours une action avant un appel de type POST, PUT, DELETE.

S'il s'agit d'un formulaire de connexion par exemple:

const loginAction = usePostLogin() // Ici, usePostLogin est un useMutation de React-Query
const router = useRouter()
const { handleSubmit, register, formState } = useForm<LoginForm>() // useForm vient de React-Hook-Form

const onSubmit = (login: LoginForm) => {
  loginAction.mutate(login, {
    onSuccess: () => {
      toast.success('Welcome')
      router.push('/')
    },
    onError: () => toast.error('Something went wrong')
  })
}

return (
  <>
     <form onSubmit={handleSumit(onSubmit)}>
       <input type="text" {...register('username')} />
       <input type="password" {...register('password')} />
       <button type="submit">Sign in</button>
     </form>
  </>
)

Ici c'est l'action de submit le formulaire qui va déclencher notre mutation via le hook.

Mais l'organisation via des hooks me permet de déplacer cette logique de page en page facilement.

De même quand j'ai un soucis avec un appel, je sais exactement dans quel composant je dois effectuer mes modifications étant donné mon organisation.

Une meilleure organisation frontend

En appliquant ces principes simples mais puissants, vous allez gagner en lisibilité, en maintenance et surtout en scalabilité sur vos projets React/Next.js.

Croyez-en mon expérience : une architecture claire et DRY fait toute la différence quand il s'agit de livrer vite, proprement, et sans surprises en production.

Le code, c'est avant tout un métier d'anticipation : préparez vos bases sérieusement, et vos projets vous remercieront.

FAQ

Pourquoi utiliser React-Query plutôt qu'un simple fetch dans chaque composant ?

React-Query gère un système de cache par clé qui évite de refaire le même appel réseau si plusieurs composants ont besoin des mêmes données simultanément. Cela réduit la charge serveur et simplifie la gestion des états de chargement et d'erreur.

Comment nommer mes hooks pour que l'équipe s'y retrouve facilement ?

L'auteur propose une convention basée sur le type de résultat attendu : useList pour un tableau, useGet pour un objet, useSave pour une création ou mise à jour selon la présence d'un ID, et useDelete pour la suppression. Ce nommage rend le code auto-documenté sans avoir à ouvrir chaque fichier.

Est-ce que je dois créer un hook par requête même si la requête est très simple ?

Oui, c'est justement l'idée centrale du principe DRY appliqué ici. Un hook centralisé, même simple, évite de dupliquer la logique sur chaque page et garantit qu'une modification future n'est à faire qu'à un seul endroit.

Comment gérer l'état de chargement quand les données ne sont pas encore disponibles au premier rendu ?

React-Query expose un état isLoading directement dans le hook, ce qui permet d'afficher un indicateur visuel comme un spinner le temps que la réponse arrive, sans logique supplémentaire à écrire dans le composant.

Faut-il organiser ses hooks dans des sous-dossiers par modèle de données ?

C'est la structure recommandée dans cet article : un dossier par entité métier comme users ou products, avec un fichier par type d'opération. Cela facilite l'import et la localisation rapide du hook à modifier en cas de bug.

#typescript#react#methodologie

user picture

Alexandre P.

Développeur passionné depuis plus de 20 ans, j'ai une appétence particulière pour les défis techniques et changer de technologie ne me fait pas froid aux yeux.