useDeferredValue: mostrar la versión vieja
¿Cómo muestra React el resultado anterior sin usar un temporizador?
Al terminar vas a poder explicar
- Explicar la diferencia real entre useDeferredValue y un debounce
- Elegir entre useTransition y useDeferredValue según quién es dueño del valor
- Detectar el render doble que produce y decidir si importa
useTransition sirve cuando vos disparás el update. Pero muchas veces el valor caro llega: es una prop, o sale de un hook que no controlás. No hay ningún setState que puedas envolver.
Para eso está useDeferredValue. En vez de marcar el update, marca el valor.
Devuelve el valor anterior, a propósito
1function Galeria({ consulta }) {2 const consultaDiferida = useDeferredValue(consulta);3 4 // La lista pesada se dibuja con la versión que puede ir atrasada.5 return <ResultadosPesados consulta={consultaDiferida} />;6}Cuando consulta cambia, el hook devuelve el valor anterior y marca TransitionLane hacia la raíz. React renderiza esa pasada urgente con el valor viejo — rápido, porque la lista no cambió — y después arranca una pasada de baja prioridad donde el hook ya devuelve el valor nuevo.
Dos pasadas: valor viejo primero, valor nuevo después
AGENDADOpaso 1 / 171Sin hooks en este paso.
Árbol de fibers
- <HostRoot>
Salida
Nada en el host todavía. React no commiteó ningún cambio.
createRoot().render()
Montaje inicial. No hay árbol previo, así que todo se va a crear desde cero.
VER EL CÓDIGO QUE SE ESTÁ TRAZANDO
1function Galeria() {2 const [texto, setTexto] = useState("a");3 const diferido = useDeferredValue(texto);4 5 // El input responde con "texto" (siempre al día).6 // La lista pesada se dibuja con "diferido" (puede ir atrasada un render).7 return (8 <>9 <input value={texto} onChange={e => setTexto(e.target.value)} />10 <ListaPesada consulta={diferido} />11 </>12 );13}Con el foco acá: ← → un paso · espacio reproduce · Inicio/Fin van a los extremos.
En la traza, mirá el paso useDeferredValue() → "a" (valor viejo) y después (alcanzó). Y el panel de salida: el segundo pintado muestra inmediato: b · diferido: a. Ese desfase de un render es exactamente el punto.
No es un debounce
Esta confusión es la más común, y la diferencia es importante:
- Un debounce espera un tiempo fijo. Si tu máquina está libre, igual esperás 300 ms. Si está saturada, el trabajo pesado igual bloquea cuando finalmente arranca.
useDeferredValueno espera nada. Procesa apenas puede. Y si durante esa pasada llega algo urgente, React la abandona y la rehace después con el valor más nuevo.
En una máquina rápida, el usuario casi no ve el estado atrasado. En una lenta, ve el valor viejo un rato pero la interfaz nunca se traba. Un debounce se comporta igual de mal en las dos.
Marcar que está viejo
Como podés comparar el valor real con el diferido, sabés cuándo hay desfase — sin isPending:
1const consultaDiferida = useDeferredValue(consulta);2const estaViejo = consulta !== consultaDiferida;3 4return (5 <div style={{ opacity: estaViejo ? 0.6 : 1 }}>6 <ResultadosPesados consulta={consultaDiferida} />7 </div>8);Bajar la opacidad de los resultados viejos mientras llegan los nuevos es mejor experiencia que un spinner: el usuario sigue viendo contenido en vez de un hueco.
Antes de seguir · predecí
Usás useDeferredValue y la interfaz sigue trabándose igual. ¿Qué falta revisar primero?
Lo que te llevás
useDeferredValue devuelve el valor anterior y agenda una pasada de baja prioridad para adoptar el nuevo. No es un debounce: no hay espera fija y lo urgente siempre pasa primero. Sirve cuando tenés el valor pero no el setter — y el componente que lo consume tiene que estar memoizado.
Antes de marcarla, comprobá
- ¿Podés explicar explicar la diferencia real entre useDeferredValue y un debounce?
- ¿Podés explicar elegir entre useTransition y useDeferredValue según quién es dueño del valor?
- ¿Podés explicar detectar el render doble que produce y decidir si importa?