Back

Erreurs Courantes avec les React Server Components

Erreurs Courantes avec les React Server Components

Vous avez adopté l’App Router de Next.js. Les Server Components sont activés par défaut. Tout devrait être plus rapide, plus simple, plus efficace.

Au lieu de cela, vous déboguez des erreurs d’hydratation à 2 heures du matin, vous vous demandez pourquoi votre bundle a grossi, et vous remettez en question l’emplacement de cette directive 'use client'.

Les React Server Components représentent un changement fondamental dans le fonctionnement des applications React. La frontière serveur/client n’est plus seulement une préoccupation de déploiement—c’est une décision architecturale que vous prenez avec chaque composant. Se tromper à ce niveau conduit à des bugs subtils, des régressions de performance, et du code qui combat le framework au lieu de l’exploiter.

Cet article couvre les erreurs les plus courantes liées aux React Server Components que j’ai observées dans des bases de code en production et comment les éviter.

Points Clés à Retenir

  • Les Server Components sont activés par défaut dans l’App Router de Next.js—placez 'use client' aussi bas que possible dans l’arbre de composants pour minimiser la taille du bundle.
  • Utilisez le package server-only pour empêcher l’exposition accidentelle de code serveur sensible dans le bundle client.
  • Convertissez toujours les valeurs non sérialisables (comme les fonctions et les instances de classe) avant de les transmettre des Server Components aux Client Components.
  • Les Server Actions ('use server') sont des endpoints de type RPC, pas des Server Components—validez toutes les entrées et ne faites jamais confiance aux données client.
  • Soyez explicite sur le comportement de mise en cache avec revalidate ou cache: 'no-store' car les valeurs par défaut de Next.js ont changé entre les versions.

Comprendre les Server Components vs Client Components

Avant d’aborder les pièges, établissons les bases. Dans l’App Router, les composants sont des Server Components par défaut. Ils s’exécutent sur le serveur, n’ont pas accès aux APIs du navigateur, et n’envoient aucun JavaScript au client.

Les Client Components nécessitent la directive 'use client'. Ils peuvent utiliser des hooks comme useState et useEffect, accéder aux APIs du navigateur, et gérer les interactions utilisateur.

La frontière entre les deux est l’endroit où la plupart des erreurs se produisent.

Utilisation Excessive de la Directive ‘use client’

L’erreur la plus fréquente avec les RSC de l’App Router de Next.js est de recourir à 'use client' trop tôt. Un composant a besoin de useState ? Marquez-le comme client component. Besoin d’un gestionnaire onClick ? Client component.

Le problème : 'use client' crée une frontière. Tout ce que ce composant importe devient partie du bundle client, même si ces imports auraient pu rester sur le serveur.

// ❌ La page entière devient un client component
'use client'

import { useState } from 'react'

export default function ProductPage({ product }) {
  const [quantity, setQuantity] = useState(1)
  
  return (
    <div>
      <ProductDetails product={product} />
      <ProductReviews productId={product.id} />
      <QuantitySelector value={quantity} onChange={setQuantity} />
    </div>
  )
}
// ✅ Seule la partie interactive est un client component
import ProductDetails from './ProductDetails'
import ProductReviews from './ProductReviews'
import QuantitySelector from './QuantitySelector'

export default function ProductPage({ product }) {
  return (
    <div>
      <ProductDetails product={product} />
      <ProductReviews productId={product.id} />
      <QuantitySelector /> {/* Ceci est le seul client component */}
    </div>
  )
}

Placez 'use client' aussi bas que possible dans l’arbre de composants. Isolez l’interactivité dans les plus petits composants qui en ont besoin.

Importer du Code Exclusivement Serveur dans des Client Components

Lorsqu’un client component importe un module, ce module entier (et ses dépendances) est envoyé au navigateur. Importer un client de base de données ou un fichier qui lit des secrets d’environnement ? Vous venez d’exposer du code exclusivement serveur au graphe client.

// lib/db.js
import 'server-only' // Ajoutez ceci pour empêcher les imports client accidentels

export async function getUsers() {
  return db.query('SELECT * FROM users')
}

Le package server-only (fourni par Next.js) provoque une erreur de build si le module est importé dans un client component. Utilisez-le pour tout code qui ne doit jamais atteindre le navigateur.

Transmettre des Valeurs Non Sérialisables à Travers la Frontière

Les Server Components transmettent des props aux Client Components via la sérialisation. Les fonctions, instances de classe, Map, et Set ne peuvent pas franchir cette frontière.

// ❌ Les instances de classe ne sont pas sérialisables
export default async function UserProfile({ userId }) {
  const user = await getUser(userId)
  return <ClientProfile user={user} /> // user est une instance de classe
}

// ✅ Convertir en objet simple
export default async function UserProfile({ userId }) {
  const user = await getUser(userId)
  return (
    <ClientProfile 
      user={{
        id: user.id,
        name: user.name,
        createdAt: user.createdAt.toISOString()
      }} 
    />
  )
}

Mauvaise Compréhension des React Server Actions

La directive 'use server' marque des fonctions comme Server Actions—appelables depuis le client mais exécutées sur le serveur. Elle ne transforme pas un composant en Server Component. Les Server Components n’ont besoin d’aucune directive puisqu’ils sont activés par défaut.

// Ceci est une Server Action, pas un Server Component
async function submitForm(formData) {
  'use server'
  await db.insert({ email: formData.get('email') })
}

Les Server Actions sont effectivement des endpoints de type RPC. Traitez-les comme des routes API : validez les entrées, gérez les erreurs, et ne faites jamais confiance aux données client.

Ignorer le Modèle de Mise en Cache RSC

Le comportement de mise en cache de Next.js a considérablement évolué. Ne supposez pas que les appels fetch sont mis en cache par défaut—cela varie selon la version de Next.js, la configuration du segment de route, et les paramètres d’exécution. Soyez explicite sur la fraîcheur des données.

// Soyez explicite sur les intentions de mise en cache
const data = await fetch(url, { 
  next: { revalidate: 3600 } // Cache pendant 1 heure
})

// Ou désactivez complètement
const data = await fetch(url, { cache: 'no-store' })

Utilisez revalidatePath() et revalidateTag() dans les Server Actions pour invalider les données mises en cache après des mutations. Le modèle de mise en cache RSC nécessite des décisions intentionnelles sur la fraîcheur des données.

Conclusion

Les React Server Components récompensent une réflexion approfondie sur l’endroit où le code s’exécute. Privilégiez les Server Components par défaut. Repoussez les frontières client vers le bas. Sérialisez les données à la limite. Validez les entrées des Server Actions. Soyez explicite sur la mise en cache.

Le modèle mental prend du temps à intérioriser, mais les bénéfices—bundles plus petits, chargements plus rapides, récupération de données simplifiée—valent l’investissement.

FAQ

Partiellement. Les Server Components ne peuvent pas utiliser les hooks d'état ou d'effet comme useState ou useEffect car ils s'exécutent uniquement sur le serveur. Cependant, des hooks comme useContext sont pris en charge. Si votre composant a besoin d'état, d'effets, ou d'APIs du navigateur, vous devez ajouter la directive use client pour en faire un Client Component. Gardez ces parties interactives aussi petites et isolées que possible.

Demandez-vous si le composant a besoin d'interactivité, d'APIs du navigateur, ou de hooks React comme useState ou useEffect. Si oui, il doit être un Client Component. S'il ne fait que rendre des données ou récupérer depuis une base de données, gardez-le comme Server Component. En cas de doute, commencez avec un Server Component et ajoutez use client uniquement lorsque le build ou l'exécution l'exige explicitement.

La cause la plus courante est le placement de use client trop haut dans votre arbre de composants. Lorsqu'un parent devient un Client Component, tous ses imports rejoignent le bundle client. Auditez vos directives use client et repoussez-les vers les plus petits composants interactifs. Vérifiez également les imports accidentels de bibliothèques exclusivement serveur dans le code client.

La directive use client marque un composant pour s'exécuter dans le navigateur avec accès aux hooks et APIs du navigateur. La directive use server marque une fonction comme Server Action, qui est appelable depuis le client mais s'exécute sur le serveur. Les Server Components n'ont besoin d'aucune directive car ils sont activés par défaut dans l'App Router de Next.js.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay