Comment designer ses composants React

Comment designer ses composants React
Alexandre P. dans Dev - mis à jour le 09-03-2025

Découvrez comment créer des composants React fullstack puissants et réutilisables grâces à de simples règles et bonnes pratiques.

Rappel sur l'intérêt de faire du React

On ne code pas en React parce que c'est un framework à la mode, parce que c'est simple ou compliqué, ou encore parce que c'est stylé.

Le premier intérêt lorsque l'on utilise React, c'est que les composants sont designés pour être réutilisables, que le code généré est optimisé (dans ses cycles de render, dans sa génération HTML, etc...).

Avec l'avènement de Next.js, on a commencé à faire des pages où le rendu server-side permettait de préparer en amont le DOM (le résultat HTML) avant même que le front ait besoin d'interpréter le JSX. Cette approche a plusieurs avantages, une réduction notable du chargement de la page côté front (on va juste rafraîchir les composants et écouter les événements du lifecycle), mais aussi apporte des améliorations pour la SEO.

En revanche, la plupart des projets Next.js où je suis intervenu avaient souvent un problème de conception : les composants ne sont pas designés pour le server-side rendering.

L'approche React-Query

Il y a plusieurs façons d'exploiter Next.js sur les projets. Certains développeurs utilisent Next.js uniquement pour faire des requêtes vers des API, passent les réponses au client sous forme déshydratée (ou sérialisée) afin d'utiliser ensuite React-Query pour récupérer ce "cache" côté front.

// Composant back
import { HydrationBoundary, QueryClient, dehydrate } from "@tanstack/react-query"

export const BackendComponent = async () => {
  const data = await fetch("http://api.weather.url/data")
  const json = await data.json()

  const queryClient = new QueryClient()
  queryClient.setQueryData(["weather-data"], json)
  
  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <FrontendComponent />
    </HydrationBoundary>
  )
}

// Composant front
"use client"

import { useQuery } from "@tanstack/react-query"

export const FrontendComponent = () => {
  const { data: myData } = useQuery({
    queryKey: ["weather-data"],
    queryFn: () => fetch("http://api.weather.url/data").then((res) => res.json()),
    initialData: undefined
  })

  return (
    <div>
      {myData.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  )
}

Dans cet exemple, le server side ne prépare pas le DOM, c'est bien le front qui va se charger de ça. En revanche il a passé les données au front pour éviter d'ajouter le temps de latence au moment du chargement côté navigateur. Dans certains cas, ce procédé permet de garder une opacité sur les appels API (empêcher l'exposition des clés, token etc...).

Pour autant, il n'y a aucune optimisation SEO dans cet exemple. Lorsque l'on va analyser le code source de la page au chargement, on constatera que les données passées à React-Query sont en fait un JSON qui passe dans le code source et qui sera ensuite parsé côté front.

L'approche Server-Side Rendering

On peut essayer de rendre le DOM côté serveur pour éviter au front d'avoir à le faire de son côté et ajouter de la latence alors que les données sont déjà chargées. Et cette approche est aussi bénéfique pour le SEO du site, car le DOM a beaucoup plus de valeur pour le crawler que de la donnée brute dans un array. Il est possible de procéder de la sorte :

Depuis un fichier serverSideAction.ts :

"use server"

export const getDataFromServerSideAction = async () => {
  const data = await fetch("http://api.weather.url/data")
  const result = await data.json()
  return result
}

Dans la page:

import ...

export const MyComponent = ({ item }: { item: any }) => {
  return <div>{item.name}</div>
}

export const BackendPage = async () => {
  const json = await getDataFromServerSideAction()

  return ( 
    <div>
      {json.map((item) => (
        <MyComponent key={item.id} item={item} />
      ))}
    </div>
  )
}

Dans ce cas, le composant sera rendu côté serveur.

Ce qu'il faut savoir concernant les composants

J'utilise souvent les classes en TypeScript afin d'ajouter des méthodes (utilitaires, etc...) ou champs virtuels, clés camelCase etc... au lieu de manipuler directement les réponses API ou base de données.

Cependant, si vous procédez de la sorte, il est primordial de créer des méthodes de sérialisation afin de les rendre exploitables dans vos composants JSON. En général, je crée la méthode statique "fromDatabaseObject" pour me rendre une instance de ma classe ou encore une méthode "toDatabaseObject" pour rendre un plain object au format base de données.

Le plain object, c'est l'ingrédient essentiel de vos composants afin qu'ils fonctionnent toujours en fullstack.

C'est à dire que vous ne devez passer en props, que des valeurs qui sont :

  • des primitives (string, number, boolean...)
  • des plain objects (un JSON)

Lorsque vous passez des classes avec des méthodes etc., vous ne pouvez pas render ce composant depuis le backend, ceci peut même lever une erreur !

Le cas des données provenant du projet

Si vous voulez récupérer en server-side les données provenant du même projet, ne repassez pas par les API du serveur Next pour fetch. Dans votre server-side action, appelez directement votre base de données.

Dans le cas contraire, vous allez créer une espèce de boucle de latence qui repasse par du network alors que le projet se parle à lui-même. Dans ce cas, vous ne faites qu'ajouter de la latence inutilement.

#react#nextjs#serverside#seo#component

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.


Votre vie privée

Nous utilisons des cookies pour améliorer votre expérience sur notre site, analyser notre trafic et personnaliser les publicités. En cliquant sur "Accepter", vous consentez à l'utilisation de tous les cookies. Vous pouvez également choisir de refuser en cliquant sur le bouton "Refuser".