Menu

useDeferredValue in React: lasciare indietro le parti lente

useDeferredValue ti dà una copia di un valore a cui è permesso restare indietro durante i rendering pesanti, così un input resta reattivo mentre una lista lenta si mette in pari. Impara come funziona, il valore iniziale di React 19 e il confronto con useTransition e il debouncing.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

useDeferredValue ti dà una copia di un valore a cui è permesso restare indietro. Quando il valore cambia, React prima ridisegna lo schermo con la vecchia copia, il che mantiene la digitazione istantanea, poi renderizza in background la parte lenta con il nuovo valore.

Scrivi una parola velocemente. L'input tiene il passo e la lista si attenua mentre è indietro. La Console registra una riga SlowList rendering per ogni lettera, perché SlowList registra appena React inizia a renderizzarlo, ma le liste per i valori intermedi non arrivano mai sullo schermo: ogni nuova lettera ha interrotto il rendering in background, e React è ripartito con il testo più recente. Cambia query={deferredQuery} in query={query} e scrivi di nuovo: ora ogni tasto aspetta un quarto di secondo per la lista.

La sintassi

const deferredValue = useDeferredValue(value);
const deferredValue = useDeferredValue(value, initialValue); // React 19
  • value è qualsiasi cosa tu renderizzi: una stringa da un input, un numero, un oggetto. I valori primitivi funzionano meglio, perché un nuovo oggetto a ogni rendering conta sempre come un cambiamento.
  • Il valore restituito è uguale a value la maggior parte del tempo. Subito dopo che value cambia, contiene ancora il valore precedente per un rendering, e si mette in pari in un rendering in background.

Come funziona

Ogni cambiamento produce due rendering:

  1. Un rendering urgente con il nuovo value e il vecchio deferredValue. L'input mostra il nuovo testo. I componenti che ricevono solo deferredValue ottengono le stesse props di prima.
  2. Un rendering in background con il nuovo deferredValue. Questo è quello lento. React lo renderizza a pezzi e lo abbandona se value cambia di nuovo prima che finisca, poi riparte con il valore più recente.

Puoi osservarlo registrando entrambi i valori:

Scrivi una sola lettera, per esempio a, e la Console stampa altre due righe: prima text = "a" con deferred = "", poi entrambi "a". Quel primo rendering è ciò che mantiene veloce la digitazione, ma solo se la parte lenta può saltarlo. Per questo SlowList nel primo esempio è racchiuso in memo: durante il rendering urgente la sua prop query non è cambiata, quindi React lo salta. Senza memo, il rendering urgente eseguirebbe comunque l'intera lista lenta, solo con il vecchio testo, e non guadagneresti nulla.

Mostrare contenuto vecchio

Mentre il rendering in background è in corso, value e il valore differito sono diversi. Confrontali per dire all'utente che il contenuto non è aggiornato, senza nasconderlo:

const isStale = query !== deferredQuery;

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

Di solito è più gradevole di uno spinner: i vecchi risultati restano leggibili e cambiano sul posto quando quelli nuovi sono pronti. La stessa idea funziona con Suspense: se il valore differito fa sospendere un componente mentre carica dati, React continua a mostrare il vecchio contenuto invece del fallback finché il nuovo contenuto non è pronto (vedi Suspense e lazy).

Il valore iniziale (React 19)

Per default il primo rendering non ha nulla rispetto a cui restare indietro, quindi il valore differito è fin dall'inizio il valore reale. Da React 19 puoi passare un secondo argomento. Al primo rendering l'hook restituisce initialValue, e poi React renderizza di nuovo in background con il valore reale. Questo ti permette di mostrare subito qualcosa di leggero e completare la parte costosa subito dopo.

La Console mostra due rendering: prima con "", poi con il testo completo. Togli il secondo argomento e viene registrato un solo rendering, subito con il testo completo.

useDeferredValue vs useTransition

Entrambi segnano del lavoro come non urgente e usano lo stesso meccanismo di rendering. La differenza è dove li metti:

  • useTransition racchiude l'aggiornamento. Chiami startTransition(() => setQuery(text)), quindi ti serve il setter. Ottieni isPending senza sforzo.
  • useDeferredValue racchiude il valore. Lo chiami dove il valore viene usato, quindi funziona quando il valore arriva come prop o da un hook che non controlli. Calcoli tu lo stato di attesa con 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 vs debounce e throttle

Il debouncing aspetta un tempo fisso dopo l'ultimo cambiamento; il throttling aggiorna al massimo una volta per intervallo. Entrambi scelgono un ritardo in anticipo, quindi su un portatile veloce rendono la pagina più lenta del necessario, e su un telefono lento il ritardo potrebbe comunque essere troppo breve.

useDeferredValue non ha alcun ritardo. Il rendering in background parte immediatamente, quindi su un dispositivo veloce la lista si aggiorna quasi subito, e su un dispositivo lento React mantiene l'input reattivo e renderizza la lista tutte le volte che può. Il lavoro in background è anche interrompibile, cosa che un rendering con debounce non è: una volta avviato, un rendering con debounce blocca finché non termina.

Ciò che non fa è ridurre le richieste di rete. Se ogni nuovo valore avvia un fetch, il valore differito cambia comunque a ogni rendering che si completa, quindi applica il debounce alla richiesta stessa quando il volume di richieste conta.

Errori comuni

  • Dimenticare memo. Come mostrato sopra, il componente lento deve poter saltare il rendering urgente. Se non è racchiuso in memo, o una delle sue altre props è un nuovo oggetto a ogni rendering, si renderizza due volte per ogni tasto invece di una.
  • Passare un nuovo oggetto. useDeferredValue({ query }) crea un nuovo oggetto a ogni rendering, quindi il valore differito sembra sempre cambiato e React avvia ogni volta un rendering in background in più. Rendi differito il valore primitivo (useDeferredValue(query)) e costruisci gli oggetti a partire da esso.
  • Aspettarsi meno richieste. Un componente che recupera dati in base al valore differito recupera comunque per ogni valore il cui rendering si completa.

Quando usarlo

Usa useDeferredValue quando un input guida una parte della pagina lenta da renderizzare, la parte lenta può essere memorizzata, e ti va bene mostrare per un momento risultati un po' vecchi: risultati di ricerca, un grafico che si ridisegna da un filtro, un'anteprima di testo formattato. Se la parte lenta è lenta per un motivo che puoi risolvere, come renderizzare 5.000 righe quando ne sono visibili 50, risolvi prima quello. Per la versione di questo schema in cui possiedi il setter, vedi useTransition.

Domande frequenti

Cosa fa useDeferredValue?

Restituisce una versione di un valore che può restare indietro rispetto a quello reale. Quando il valore cambia, React prima renderizza con il vecchio valore differito, poi renderizza di nuovo in background con quello nuovo, e quel rendering in background può essere interrotto.

Perché useDeferredValue non rende più veloce la mia lista?

Il componente lento deve essere racchiuso in memo e ricevere il valore differito. Altrimenti il rendering urgente renderizza comunque il componente lento, solo con il vecchio valore, e la digitazione resta lenta.

Qual è la differenza tra useDeferredValue e useTransition?

useTransition racchiude l'aggiornamento di stato, quindi ti serve accesso al setter. useDeferredValue racchiude un valore, quindi funziona quando ricevi solo il valore, per esempio come prop o da un hook che non controlli.

useDeferredValue è un debounce?

No. Non c'è alcun ritardo fisso. React avvia il rendering differito subito dopo quello urgente, e su un dispositivo veloce il ritardo si vede appena. Inoltre da solo non riduce le richieste di rete.

Qual è il secondo argomento di useDeferredValue?

Da React 19 puoi passare un initialValue. Al primo rendering l'hook lo restituisce, poi React renderizza di nuovo in background con il valore reale.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA