useSyncExternalStore permite que un componente lea datos que residen fuera de React sin riesgo de los fallos visuales que puede introducir el renderizado concurrente. Reemplaza el antiguo patrón de extraer valores externos al estado local con useEffect, un patrón que puede causar “tearing” (desgarro visual): partes de la interfaz de usuario que muestran valores diferentes durante el mismo renderizado.
Por qué el antiguo patrón falla bajo concurrencia
Antes de React 18, la receta común era:
const [value, setValue] = useState(initial);
useEffect(() => {
const unsubscribe = externalSource.subscribe(v => setValue(v));
return unsubscribe;
}, []);
El efecto se ejecuta después de que el componente se ha renderizado. Cuando el renderizador concurrente de React decide pausar, abortar o repetir un renderizado, el efecto puede ejecutarse en un punto diferente al de la interfaz de usuario que lee el estado. El resultado es un desajuste: un componente puede haber leído el valor antiguo, otro el nuevo, y la pantalla muestra brevemente datos inconsistentes. Este “tearing” es inofensivo en un mundo puramente síncrono, pero se convierte en un error visible cuando React renderiza múltiples versiones de la interfaz de usuario en paralelo.
Cómo useSyncExternalStore resuelve el problema
El hook requiere dos callbacks:
- subscribe – registra un oyente y devuelve una función que lo elimina.
- getSnapshot – devuelve el valor actual de la fuente externa.
React llama a subscribe cuando el componente se monta y lo elimina al desmontarse. Durante cada renderizado, invoca getSnapshot y garantiza que todos los componentes que utilizan el mismo almacén externo lean exactamente el mismo snapshot. Debido a que la lectura ocurre dentro de la fase de renderizado, el renderizador puede coordinar las actualizaciones en todo el árbol, eliminando el tearing.
Librerías como Zustand y Redux ya envuelven sus stores con este hook, razón por la cual funcionan directamente con las características concurrentes de React 18.
Reglas para evitar que el hook se comporte incorrectamente
- Devuelve solo lo que el componente necesita – un objeto grande obliga a React a comparar estructuras profundas en cada actualización. Un snapshot reducido mantiene la detección de cambios económica.
- Preserva la identidad referencial – si el valor externo no ha cambiado, getSnapshot debe devolver la misma referencia de objeto. Devolver un objeto nuevo en cada llamada activa un bucle de renderizado infinito porque React detecta un cambio en cada renderizado.
- Limita su uso a fuentes externas – el estado local del componente pertenece a
useStateouseReducer. Usar el hook de almacén externo para valores puramente internos añade una indirección innecesaria.
Cuándo recurrir a useSyncExternalStore
- Envolver APIs del navegador (tamaño de la ventana, estado de conexión a la red, media queries).
- Conectar un almacén de JavaScript puro que no conoce a React.
- Reemplazar el prop-drilling con una fuente global de solo lectura a la que se suscriban muchos componentes.
El contraargumento
El patrón useState + useEffect sigue funcionando para datos simples que cambian con poca frecuencia, especialmente cuando una aplicación no tiene habilitadas las funciones concurrentes. Es un enfoque de menor nivel que a algunos desarrolladores les resulta más fácil de razonar porque la lógica de suscripción reside explícitamente en el efecto. Sin embargo, el compromiso es el riesgo de tearing una vez que se activa el renderizado concurrente. Para proyectos que ya utilizan las nuevas capacidades de React 18, la seguridad adicional de useSyncExternalStore compensa el mínimo código adicional.
Qué vigilar a continuación
- Verifica que el getSnapshot de tu store devuelva una referencia estable; un
console.lográpido de la referencia durante el desarrollo puede exponer la recreación accidental de objetos. - Mantén la función de suscripción ligera – el trabajo pesado debe ocurrir dentro de la fuente externa, no en el callback pasado a React.
- Prueba los componentes bajo el modo concurrente de React (por ejemplo, usando
actconReactDOM.createRoot) para confirmar que no aparezca ningún tearing.
Una demo en vivo muestra el hook en acción, sincronizando un contador simple almacenado fuera de React y actualizando múltiples componentes sin fallos visuales. La demo está disponible en https://usesyncexternalstore.vercel.app/. El código fuente reside en GitHub, y un artículo más extenso explica los detalles de la implementación.
Conclusión: En un mundo donde React puede renderizar partes de la interfaz de usuario fuera de orden, useSyncExternalStore es el puente confiable entre los datos externos y el renderizado de componentes. Preserva la consistencia visual, escala a almacenes globales y ya está integrado en las librerías de gestión de estado más populares. Adóptalo siempre que necesites leer un estado que no sea de React, y mantén los snapshots pequeños y estables para aprovechar todos sus beneficios.
