← retour Rack serveur vintage dans une salle faiblement éclairée avec une lumière dorée chaude traversant une fenêtre étroite, grain de film 35 mm

React Server Components Expliqués Sans les Grands Gestes

Au début de 2023, un client à moi, une startup fintech basée à Canary Wharf, m'a confié une codebase Next.js 13 qu'une agence précédente avait construite. Elle utilisait le nouveau répertoire app/. Sympa. Sauf que les erreurs d'hydratation étaient partout, le bundle faisait 340 KB compressé, et personne de l'ancienne équipe ne pouvait expliquer pourquoi ils avaient mis « use client » sur littéralement chaque fichier. Quand je leur ai demandé au sujet de React Server Components, ils ont dit : « Oh oui, on utilise ça. » Ils n'utilisaient pas ça.

C'est le problème avec les RSC en ce moment. Tout le monde prétend les comprendre. Presque personne ne les comprend réellement. Alors laissez-moi vous donner la version que j'aurais aimé exister au début de 2023.

Ce que les RSC Sont Réellement (Pas la Version Marketing)

React Server Components sont des composants qui s'exécutent uniquement sur le serveur et n'envoient jamais leur JavaScript au navigateur. Un point c'est tout.

Pas « rendu côté serveur ». Pas « pré-rendu ». Ces choses existaient avant les RSC et elles fonctionnent différemment. Avec le SSR traditionnel (ce que le routeur pages de Next.js fait), vos composants se rendent sur le serveur pour produire du HTML, mais ensuite le même JavaScript est envoyé au client pour que React puisse « hydrater » la page, attacher des écouteurs d'événements et prendre le contrôle.

Les RSC sautent entièrement la deuxième partie. Le composant s'exécute sur le serveur, se rend, envoie sa sortie en tant que charge utile sérialisée au client, et puis... c'est tout. Aucun JavaScript pour ce composant ne finit jamais dans le navigateur.

L'effet pratique : si vous avez un composant <ProductDescription> qui utilise un analyseur markdown de 40 KB pour rendre du texte, et que vous en faites un composant serveur, ces 40 KB ne vont jamais à l'utilisateur. Le HTML analysé le fait. C'est tout.

Le RFC original de l'équipe React vaut vraiment la peine d'être lu si vous voulez la justification complète. C'est dense mais honnête.

Le modèle mental qui a enfin tout clarifié pour moi

Pensez à votre arborescence de composants comme deux mondes distincts qui se trouvent être cousus ensemble.

Monde 1 (Serveur). A accès complet à votre base de données, votre système de fichiers, vos variables d'environnement, vos secrets. Ne peut pas utiliser useState, useEffect, ou aucune API du navigateur. Ne peut pas attacher d'écouteurs d'événements.

Monde 2 (Client). S'exécute dans le navigateur. Peut utiliser tous les hooks React que vous connaissez. Ne peut pas communiquer directement avec votre base de données. Envoie des requêtes aux API à la place.

Avant les RSC, chaque composant vivait dans le Monde 2, même s'il était rendu côté serveur en premier. Avec les RSC, vous pouvez maintenant placer explicitement des composants dans le Monde 1. Ces composants peuvent rendre des composants du Monde 2 en tant qu'enfants, en leur passant des props sérialisables. Mais les composants du Monde 2 ne peuvent pas rendre les composants du Monde 1. La limite est unidirectionnelle.

C'est là que les gens se trompent : vous n'ajoutez pas de directive pour créer un composant serveur. Dans le répertoire app/ de Next.js, tout est un composant serveur par défaut. Vous optez pour le client avec "use client". C'est l'inverse de ce que la plupart des gens supposent.

Seahawk a eu un projet l'année dernière, un grand catalogue e-commerce pour un détaillant britannique, où nous avons audité une soixantaine de composants. Il s'avère qu'environ 40 d'entre eux avaient "use client" sans raison. Le supprimer a réduit le bundle JavaScript d'environ 28%. Le travail d'un après-midi.

Ce que vous pouvez et ne pouvez pas faire (Une analyse concrète)

Les composants serveur peuvent :

  • Récupérer les données directement avec async/await, sans useEffect, sans états de chargement, juste const data = await db.query(...)
  • Importer des bibliothèques lourdes réservées au serveur (comme gray-matter pour l'analyse du frontmatter, ou les générateurs PDF) sans affecter le bundle client
  • Lire depuis le système de fichiers à l'aide du module fs de Node
  • Accéder aux variables d'environnement que vous ne voulez pas exposer au navigateur
  • Passer les données aux composants client en tant que props

Les composants serveur ne peuvent pas :

  • Utiliser useState ou useReducer
  • Utiliser useEffect ou useLayoutEffect
  • Attacher des gestionnaires d'événements (pas de onClick, pas de onChange)
  • Utiliser des API du navigateur (window, document, localStorage)
  • Utiliser React Context directement (bien qu'il existe des modèles pour contourner cela)

Les composants client peuvent faire tout ce que les composants serveur ne peuvent pas, mais :

  • Ils ne peuvent pas appeler directement votre base de données
  • Ils ne peuvent pas utiliser de packages réservés au serveur
  • Leur code est expédié vers le navigateur

Cette phrase ne concerne pas seulement la performance. Il s'agit de savoir où le code s'exécute et ce à quoi il a accès. Cette façon de voir les choses est plus utile que de penser en termes de « rapide » vs « lent ».

La Récupération de Données est Là où les RSCs Brillent Vraiment

C'est la partie que j'aime sincèrement. Avant les RSCs, récupérer des données dans une app Next.js signifiait généralement l'une de ces options : getServerSideProps, getStaticProps, un appel useEffect côté client, ou une combinaison des trois avec un hook personnalisé pour gérer l'état de chargement. C'était fouillis.

Avec les RSCs, tu fais juste... récupérer. À l'intérieur du composant. Au niveau supérieur.

`` async function ProductPage({ id }) { const product = await getProduct(id); // calls your DB directly return <ProductDetails product={product} />; } ``

Pas de props drilling depuis une fonction au niveau page. Pas de spinners de chargement pour des données qui auraient pu être prêtes à l'arrivée. Le composant est asynchrone, il attend ce dont il a besoin, et il s'affiche.

Et c'est là que ça devient vraiment intéressant : puisque chaque composant serveur peut récupérer ses propres données de façon indépendante, tu évites l'ancien problème du « composant Dieu » où une seule fonction au niveau supérieur devait orchestrer toutes les données de la page entière. Les composants deviennent autonomes. La récupération parallèle se fait naturellement quand tu attends Promise.all() ou quand les composants frères récupèrent indépendamment.

J'ai utilisé ce motif sur un projet de tableau de bord pour une entreprise de logistique à Birmingham l'année dernière. Chaque widget, statistiques d'expédition, alertes de retard, résumé des coûts, était son propre composant serveur asynchrone. Pas d'état de chargement partagé, pas de props drilling, pas de conditions de concurrence. La page est passée d'un LCP de 2,1 secondes à 0,9 secondes. Pas seulement grâce aux RSCs, mais les RSCs ont rendu l'architecture correcte beaucoup plus facile à atteindre.

Le Pipeline de Rendu (Ce qui se Passe Réellement)

Quand un utilisateur demande une page dans une app Next.js 14 utilisant le routeur app/, voici la séquence approximative :

  1. Next.js exécute tes composants serveur sur le serveur
  2. Ces composants produisent un format spécial appelé React Server Component Payload (RSC Payload), pas du HTML brut, mais une description sérialisée de l'arborescence de l'UI
  3. Next.js utilise ce payload pour générer le HTML initial (pour le premier affichage)
  4. Ce HTML est envoyé au navigateur
  5. Le RSC Payload est également envoyé, et le runtime React côté client l'utilise pour hydrater seulement les composants clients dans l'arborescence
  6. Les composants clients reçoivent leur JavaScript, s'hydratent, et deviennent interactifs

L'étape 5 est ce qui rend les RSCs différentes du SSR ordinaire. Le client ne pré-affiche pas les composants serveur. Il utilise simplement le payload pour compléter l'image, puis concentre l'effort d'hydratation sur les parties interactives.

La documentation Next.js sur le rendu explique mieux le format du payload que la plupart des articles de blog que j'ai lus, si tu veux approfondir.

Où les RSCs Sont Insuffisantes (Critique Honnête)

D'accord. Ne prétendons pas que c'est tout rose.

La limite « "use client" » en cascade. Dès que tu mets « use client » sur un composant, chaque composant qu'il importe devient aussi côté client. Si tu n'y fais pas attention, un seul gestionnaire onClick peut attirer un sous-arbre étonnamment grand dans le bundle client. J'ai vu cela causer des problèmes aux développeurs juniors de l'équipe plusieurs fois.

Context est pénible. React Context ne fonctionne pas dans les composants serveur. Si tu as construit ton architecture d'app autour de Context pour le thème, l'état d'authentification ou la configuration globale, tu devras restructurer. Il existe des contournements (passer les valeurs en props, utiliser un wrapper client), mais c'est une friction que tu n'avais pas avant.

Le débogage est plus difficile. Les erreurs des composants serveur ne s'affichent pas dans la console du navigateur de la même façon. Le comportement des Error Boundary est différent. Tu dois vérifier les logs du serveur. Ce n'est pas rédhibitoire, mais si tu es habitué à ce que toutes les erreurs vivent dans DevTools, attends-toi à une courbe d'apprentissage.

Les bibliothèques tierces ne sont souvent pas prêtes. Toute bibliothèque qui utilise des hooks ou des APIs du navigateur sous le capot va casser dans un composant serveur. Tu passeras du temps à envelopper les choses dans des fichiers « use client » juste pour utiliser un sélecteur de date ou une bibliothèque d'animation. L'écosystème React rattrape son retard mais en 2024 c'est encore inégal.

Honnêtement, pour un simple site marketing ou un blog de contenu, vous n'aurez peut-être pas besoin des RSCs du tout. Si votre équipe est à l'aise avec le pages router et que vos performances sont correctes, migrer uniquement pour le support des RSCs n'en vaut pas la peine. J'ai détourné des clients de la migration plus d'une fois.

Conseils pratiques pour adopter les RSCs dans un projet existant

Si vous migrez une application Next.js existante vers le répertoire app/, voici l'ordre que je suivrais :

  1. Commencez par les composants feuilles. Les composants qui affichent des données mais ne gèrent pas l'interaction sont les plus faciles à gagner. Faites-en d'abord des composants serveur.
  2. Identifiez votre vraie surface d'interactivité. Généralement c'est plus petit que vous le pensez. Formulaires, modales, listes déroulantes. C'est votre territoire « use client ».
  3. Placez `"use client"` aussi bas que possible. Le bouton qui soumet un formulaire devrait être un composant client. La mise en page du formulaire autour de lui n'a probablement pas besoin de l'être.
  4. Analysez votre bundle avec [@next/bundle-analyzer](https://www.npmjs.com/package/@next/bundle-analyzer). Lancez-le avant et après. Si vous ne voyez pas de réduction significative du bundle, vous envoyez probablement trop au client.
  5. Ne partagez pas les modules serveur uniquement. Installez server-only depuis npm et importez-le en haut de tout module qui ne devrait jamais atteindre le navigateur. Ça lancera une erreur au moment du build si quelque chose essaie de l'importer côté client.

Le paquet server-only est une petite chose mais il nous a sauvés d'une scène de fuite de données assez embarrassante sur un projet client healthcare. Un développeur a accidentellement importé un utilitaire de base de données dans un composant qui a ensuite été marqué « use client ». Le paquet l'a attrapé au moment du build. Utilisez-le.

FAQ

Les React Server Components sont-ils la même chose que le SSR ?

Non. SSR (server-side rendering) rend les composants en HTML sur le serveur mais envoie quand même le JavaScript des composants au client pour l'hydratation. Les RSCs se rendent sur le serveur et n'envoient jamais leur JavaScript au navigateur du tout. SSR et RSCs peuvent fonctionner ensemble, et dans le répertoire app/ de Next.js, ils le font, mais ils résolvent des problèmes différents.

Ai-je besoin de Next.js pour utiliser les React Server Components ?

Techniquement non, mais pratiquement oui pour la plupart des équipes. Les RSCs nécessitent un framework qui gère l'infrastructure serveur, le routage et le pipeline de la charge utile RSC. Next.js 13+ avec le répertoire app/ est l'option la plus prête pour la production en ce moment. Remix a un modèle différent. Construire votre propre configuration est possible mais pas un bon usage de votre temps à moins que vous ne construisiez un framework vous-même.

Puis-je mélanger les composants serveur et client dans la même page ?

Oui, et c'est tout l'intérêt. Une page peut être un composant serveur (récupère les données, aucun JS envoyé), contenir un composant client pour une entrée de recherche, qui contient des composants serveur comme enfants passés via la prop children. L'arbre s'entrelace. Souvenez-vous juste : les composants serveur peuvent rendre les composants client, mais les composants client ne peuvent pas rendre directement les composants serveur.

Qu'advient-il de mes hooks personnalisés existants ?

Les hooks personnalisés qui utilisent useState, useEffect ou des API de navigateur ne peuvent s'exécuter que dans les composants client. Vous n'avez pas besoin de les réécrire, soyez juste intentionnel sur les composants qui les utilisent. Si un hook enveloppe un appel de récupération de données, envisagez si ces données pourraient être récupérées dans un composant serveur à la place et passées comme props. Souvent c'est possible, et vous finissez avec du code plus simple.

Y a-t-il un coût de performance au format de charge utile RSC ?

Il peut y en avoir un, particulièrement si vous passez de grandes quantités de données via la charge utile. La charge utile RSC n'est pas la même que JSON, c'est un format personnalisé, mais elle ajoute quand même des octets à votre réponse. Pour la plupart des applications c'est négligeable comparé aux économies de bundle JS. Pour les applications avec de très grands ensembles de données passés comme props, profilez-le. Ne supposez pas que les RSCs sont toujours plus petits sur le câble.

---

Les RSCs sont un vrai changement architectural, pas juste une nouvelle API. Le modèle mental met du temps à se stabiliser. Donnez-lui ce temps. Construisez quelque chose de petit avec le répertoire app/ avant de vous engager à un grand projet client. J'ai passé environ trois semaines sur des expériences avant de me faire confiance pour déployer en production une architecture basée sur les RSCs, et j'écrivais React depuis 2016.

Le baratin va continuer de la part de gens qui n'ont rien construit de réel avec. Au moins maintenant vous savez assez pour repérer la différence.

← retour