Menu

useDeferredValue : laisser les parties lentes en retard

useDeferredValue vous donne une copie d'une valeur autorisée à prendre du retard pendant les rendus lourds, pour qu'un champ reste réactif pendant qu'une liste lente rattrape son retard. Découvrez son fonctionnement, la valeur initiale de React 19, et sa comparaison avec useTransition et le debounce.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

useDeferredValue vous donne une copie d'une valeur autorisée à prendre du retard. Quand la valeur change, React redessine d'abord l'écran avec l'ancienne copie, ce qui garde la saisie instantanée, puis fait le rendu de la partie lente avec la nouvelle valeur en arrière-plan.

Tapez un mot rapidement. Le champ suit et la liste s'estompe tant qu'elle est en retard. La console affiche une ligne SlowList rendering par lettre, car SlowList écrit dès que React commence son rendu, mais les listes pour les valeurs intermédiaires n'arrivent jamais à l'écran : chaque nouvelle lettre a interrompu le rendu en arrière-plan, et React a recommencé avec le dernier texte. Remplacez query={deferredQuery} par query={query} et tapez à nouveau : chaque frappe attend maintenant un quart de seconde la liste.

La syntaxe

const deferredValue = useDeferredValue(value);
const deferredValue = useDeferredValue(value, initialValue); // React 19
  • value est tout ce que vous affichez : une chaîne venant d'un champ, un nombre, un objet. Les primitives fonctionnent le mieux, car un nouvel objet à chaque rendu compte toujours comme un changement.
  • La valeur renvoyée est égale à value la plupart du temps. Juste après un changement de value, elle garde la valeur précédente pendant un rendu, puis rattrape son retard dans un rendu en arrière-plan.

Comment il fonctionne

Chaque changement produit deux rendus :

  1. Un rendu urgent avec la nouvelle value et l'ancienne deferredValue. Le champ affiche le nouveau texte. Les composants qui ne reçoivent que deferredValue obtiennent les mêmes props qu'avant.
  2. Un rendu en arrière-plan avec la nouvelle deferredValue. C'est le rendu lent. React le fait par tranches et l'abandonne si value change à nouveau avant qu'il soit terminé, puis recommence avec la dernière valeur.

Vous pouvez observer cela en affichant les deux valeurs :

Tapez une seule lettre, par exemple a, et la console affiche deux lignes de plus : d'abord text = "a" avec deferred = "", puis les deux à "a". C'est ce premier rendu qui garde la saisie rapide, mais seulement si la partie lente peut le sauter. C'est pourquoi SlowList dans le premier exemple est enveloppé dans memo : pendant le rendu urgent, sa prop query n'a pas changé, donc React le saute. Sans memo, le rendu urgent exécuterait quand même toute la liste lente, simplement avec l'ancien texte, et vous n'y gagneriez rien.

Afficher un contenu périmé

Pendant que le rendu en arrière-plan est en cours, value et la valeur différée diffèrent. Comparez-les pour indiquer à l'utilisateur que le contenu n'est pas à jour, sans le masquer :

const isStale = query !== deferredQuery;

<div style={{ opacity: isStale ? 0.5 : 1, transition: 'opacity 0.2s' }}>
    <SlowList query={deferredQuery} />
</div>

C'est généralement plus agréable qu'un spinner : les anciens résultats restent lisibles et changent sur place quand les nouveaux sont prêts. La même idée fonctionne avec Suspense : si la valeur différée fait se suspendre un composant pendant qu'il charge des données, React continue d'afficher l'ancien contenu au lieu du contenu de repli jusqu'à ce que le nouveau soit prêt (voir Suspense et lazy).

La valeur initiale (React 19)

Par défaut, le premier rendu n'a rien sur quoi prendre du retard, donc la valeur différée est la vraie valeur dès le départ. Depuis React 19, vous pouvez passer un second argument. Au premier rendu, le hook renvoie initialValue, puis React refait le rendu en arrière-plan avec la vraie valeur. Cela vous permet d'afficher tout de suite quelque chose de peu coûteux et de compléter la partie coûteuse juste après.

La console montre deux rendus : d'abord avec "", puis avec le texte complet. Retirez le second argument et un seul rendu est affiché, directement avec le texte complet.

useDeferredValue ou useTransition

Les deux marquent un travail comme non urgent et utilisent le même mécanisme de rendu. La différence tient à l'endroit où vous les placez :

  • useTransition enveloppe la mise à jour. Vous appelez startTransition(() => setQuery(text)), il vous faut donc le setter. Vous obtenez isPending gratuitement.
  • useDeferredValue enveloppe la valeur. Vous l'appelez là où la valeur est utilisée, il fonctionne donc quand la valeur arrive en prop ou vient d'un hook que vous ne contrôlez pas. Vous calculez vous-même l'état « en attente » avec value !== deferredValue.
// You own the state: either works, a transition is direct
startTransition(() => setQuery(nextQuery));

// You only receive the value
function SearchResults({ query }) {
    const deferredQuery = useDeferredValue(query);
    return <SlowList query={deferredQuery} />;
}

useDeferredValue, debounce et throttle

Le debounce attend un temps fixe après le dernier changement ; le throttle met à jour au plus une fois par intervalle. Les deux choisissent un délai à l'avance : sur un ordinateur portable rapide, ils rendent la page plus lente que nécessaire, et sur un téléphone lent, le délai peut encore être trop court.

useDeferredValue n'a pas de délai. Le rendu en arrière-plan commence immédiatement, donc sur un appareil rapide la liste se met à jour presque aussitôt, et sur un appareil lent React garde le champ réactif et fait le rendu de la liste aussi souvent qu'il le peut. Le travail en arrière-plan est aussi interruptible, ce qui n'est pas le cas d'un rendu avec debounce : une fois lancé, il bloque jusqu'à la fin.

Ce qu'il ne fait pas, c'est réduire les requêtes réseau. Si chaque nouvelle valeur lance un fetch, la valeur différée change quand même à chaque rendu qui se termine, appliquez donc un debounce à la requête elle-même quand le volume de requêtes compte.

Erreurs courantes

  • Oublier memo. Comme montré plus haut, le composant lent doit pouvoir sauter le rendu urgent. S'il n'est pas enveloppé dans memo, ou si l'une de ses autres props est un nouvel objet à chaque rendu, il fait deux rendus par frappe au lieu d'un.
  • Passer un nouvel objet. useDeferredValue({ query }) crée un nouvel objet à chaque rendu, donc la valeur différée semble toujours modifiée et React lance un rendu en arrière-plan supplémentaire à chaque fois. Différez la primitive (useDeferredValue(query)) et construisez les objets à partir d'elle.
  • Attendre moins de requêtes. Un composant qui récupère des données selon la valeur différée le fait quand même pour chaque valeur dont le rendu se termine.

Quand l'utiliser

Utilisez useDeferredValue quand un champ pilote une partie de la page lente à afficher, que la partie lente peut être mémoïsée et qu'afficher des résultats légèrement anciens un instant ne vous dérange pas : résultats de recherche, graphique redessiné à partir d'un filtre, aperçu d'un texte mis en forme. Si la partie lente est lente pour une raison que vous pouvez corriger, comme afficher 5 000 lignes quand 50 sont visibles, corrigez cela d'abord. Pour la version de ce pattern où vous possédez le setter, voir useTransition.

Questions fréquentes

Que fait useDeferredValue ?

Il renvoie une version d'une valeur qui peut être en retard sur la vraie. Quand la valeur change, React fait d'abord le rendu avec l'ancienne valeur différée, puis refait le rendu en arrière-plan avec la nouvelle, et ce rendu en arrière-plan peut être interrompu.

Pourquoi useDeferredValue ne rend-il pas ma liste plus rapide ?

Le composant lent doit être enveloppé dans memo et recevoir la valeur différée. Sinon, le rendu urgent fait quand même le rendu du composant lent, simplement avec l'ancienne valeur, et la saisie reste lente.

Quelle est la différence entre useDeferredValue et useTransition ?

useTransition enveloppe la mise à jour d'état, vous avez donc besoin d'accéder au setter. useDeferredValue enveloppe une valeur, il fonctionne donc quand vous recevez seulement la valeur, par exemple en prop ou depuis un hook que vous ne contrôlez pas.

useDeferredValue est-il un debounce ?

Non. Il n'y a pas de délai fixe. React lance le rendu différé juste après le rendu urgent, et sur un appareil rapide le retard est à peine visible. Il ne réduit pas non plus à lui seul les requêtes réseau.

Quel est le second argument de useDeferredValue ?

Depuis React 19, vous pouvez passer une initialValue. Au premier rendu, le hook la renvoie, puis React refait le rendu en arrière-plan avec la vraie valeur.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

COMMENCER