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 tú disparas el update. Pero muchas veces el valor caro llega: es una prop, o sale de un hook que no controlas. 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 aquí: ← → un paso · espacio reproduce · Inicio/Fin van a los extremos.
En la traza, mira 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 esperas 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 puedes comparar el valor real con el diferido, sabes 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 · predice
Usas useDeferredValue y la interfaz sigue trabándose igual. ¿Qué falta revisar primero?
Lo que te llevas
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 tienes el valor pero no el setter — y el componente que lo consume tiene que estar memoizado.
Antes de marcarla, comprueba
- ¿Puedes explicar explicar la diferencia real entre useDeferredValue y un debounce?
- ¿Puedes explicar elegir entre useTransition y useDeferredValue según quién es dueño del valor?
- ¿Puedes explicar detectar el render doble que produce y decidir si importa?