Explorez les composants serveur, la lecture de promesses avec use et les mises à jour optimistes de React 19.
Ouvrir cette leçon dans KodokonLes React Server Components (RSC) redéfinissent la frontière client-serveur. Un composant serveur s'exécute uniquement côté serveur : son code ne rejoint jamais le bundle client, il peut être async, lire la base de données ou le système de fichiers directement, et son résultat est envoyé au navigateur sous forme d'un flux sérialisé (le payload RSC) que React fusionne avec l'arbre client. En contrepartie, il n'a ni état, ni effets, ni accès au navigateur : pas de useState, pas de useEffect, pas de gestionnaires d'événements. Il ne se re-rend jamais côté client.
// Composant serveur : jamais envoye au client.
export default async function NotesPage() {
const notes = await db.notes.findAll();
return (
<ul>
{notes.map((note) => (
<li key={note.id}>{note.title}</li>
))}
</ul>
);
}L'interactivité vit dans les composants client, marqués par la directive 'use client' en tête de fichier. Cette directive ne marque pas « un composant client » mais une frontière : tout ce que ce module importe bascule aussi côté client. Les props qui traversent la frontière doivent être sérialisables : objets simples, tableaux, promesses... mais jamais de fonctions ordinaires ni d'instances de classe.
'use client';
import { useState } from 'react';
export function LikeButton({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<button onClick={() => setCount(count + 1)}>
{count} likes
</button>
);
}React 19 introduit use, une API qui lit la valeur d'une promesse ou d'un contexte. Contrairement aux hooks, use peut être appelé dans une condition ou une boucle. Passé une promesse, il suspend le composant jusqu'à la résolution, en s'appuyant sur la frontière Suspense la plus proche. Le pattern idiomatique : un composant serveur crée la promesse sans l'attendre, la passe en prop, et un composant client la consomme avec use : le serveur commence à streamer la page sans attendre la donnée.
import { use, Suspense } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map((c) => (
<p key={c.id}>{c.text}</p>
));
}
function Page({ commentsPromise }) {
return (
<Suspense fallback={<Spinner />}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}Côté interactions, useTransition marque une mise à jour comme non urgente et expose isPending pour afficher un indicateur discret sans bloquer la saisie. useOptimistic complète le duo pour les mutations réseau : vous affichez immédiatement l'état espéré pendant qu'une action asynchrone s'exécute ; à la fin de l'action (succès ou échec), React abandonne la valeur optimiste et réaffiche l'état réel, celui de la source de vérité.
const [optimisticLikes, addOptimisticLike] =
useOptimistic(likes, (count, delta) => count + delta);
function handleLike() {
startTransition(async () => {
addOptimisticLike(1);
await saveLike();
// A la fin, React reaffiche l'etat reel.
});
}use est-elle autorisée à enfreindre ?useOptimistic quand l'action asynchrone se termine ?