React 18 añadió useSyncExternalStore, un hook que permite a los componentes leer datos de cualquier fuente que resida fuera del árbol de React —APIs del navegador, WebSockets o stores de terceros— sin los errores visuales que afectaban al antiguo patrón de reflejar esos datos con useState y useEffect. Si alguna vez has visto un parpadeo en la interfaz de usuario porque una parte mostraba un tamaño de ventana antiguo mientras otra ya reflejaba el nuevo, este hook es la solución que estabas esperando.
Por qué el estado externo necesita un nuevo hook
Los componentes de React siempre han podido suscribirse a elementos que existen más allá de su propio alcance: el objeto window, un store de Redux o una conexión WebSocket. El enfoque convencional consistía en configurar una suscripción dentro de un useEffect, enviar los valores entrantes al estado local mediante setState y dejar que React volviera a renderizar. Eso funciona bien cuando React renderiza de forma síncrona, pero React 18 introdujo el renderizado concurrente, donde un componente puede renderizarse varias veces antes de que el navegador pinte realmente. En ese modo, el patrón de "reflejar en el estado local" puede producir tearing (desincronización): diferentes partes de la interfaz de usuario leen diferentes instantáneas (snapshots) del mismo valor externo durante un único ciclo de renderizado.
useSyncExternalStore fue creado para prevenir el tearing. Solicita a la fuente externa un snapshot (el valor actual) y una función subscribe (cómo ser notificado de los cambios). React llama a subscribe cuando el componente se monta y se desuscribe automáticamente al desmontarse. Cada vez que el snapshot devuelto por getSnapshot cambia, React programa un renderizado que lee el mismo snapshot para todo el árbol de componentes, garantizando la consistencia.
Las dos funciones requeridas
| Función | Qué hace |
|---|---|
| subscribe | Recibe un callback que debe llamarse cada vez que el valor externo cambie. Devuelve una función unsubscribe que React llamará durante la limpieza (cleanup). |
| getSnapshot | Devuelve el valor actual de la fuente externa. Debe ser referencialmente estable (el mismo objeto de función en cada renderizado) para que React pueda comparar los snapshots correctamente. |
Una implementación mínima para el estado online del navegador se ve así:
function useOnlineStatus() {
return useSyncExternalStore(
(onChange) => {
window.addEventListener('online', onChange);
window.addEventListener('offline', onChange);
return () => {
window.removeEventListener('online', onChange);
window.removeEventListener('offline', onChange);
};
},
() => navigator.onLine
);
}
Cuando cualquier componente llame a useOnlineStatus(), React lo volverá a renderizar solo cuando navigator.onLine cambie realmente, y cada renderizado verá el mismo valor.
Reglas para evitar que el hook falle
Devuelve la pieza de datos más pequeña que el componente necesite.
Si el store externo contiene un objeto grande pero un componente solo necesita un campo, devuelve solo ese campo. Devolver objetos más grandes provoca renderizados innecesarios.Memoriza
getSnapshot.
Si recreas la función en cada renderizado, React la tratará como una nueva fuente y podría entrar en un bucle infinito. Envuélvela enuseCallbacko defínela fuera del componente.Evita devolver objetos nuevos en cada llamada.
Devolver un objeto nuevo (por ejemplo,{ count: store.getCount() }) crea una nueva referencia en cada renderizado, lo que hace que React piense que el snapshot ha cambiado y dispare un bucle de renderizado infinito. Utiliza primitivos, cadenas, números u objetos memorizados.No lo uses para el estado interno del componente.
useStatesigue siendo la herramienta adecuada para los datos que viven únicamente dentro del componente.useSyncExternalStoreañade una sobrecarga que no es necesaria para el estado local.
Cuándo utilizarlo
- APIs del navegador – tamaño de la ventana, media queries,
navigator.onLine, estado de la batería. - Stores agnósticos al framework – Zustand, Redux, MobX o cualquier store personalizado que exponga una API de suscripción.
- Fuentes mutables que se actualizan fuera de React – mensajes de WebSocket, eventos de cambio de IndexedDB, notificaciones de Service Worker.
Si tu fuente de datos ya vive dentro de React (por ejemplo, el estado de un componente padre), sigue usando useState o context.
Lo que dice la comunidad
Los primeros usuarios informan que useSyncExternalStore elimina el parpadeo que veían al cambiar el tamaño de las ventanas en una configuración de renderizado concurrente. Algunas librerías ya han cambiado sus hooks internos a esta API, prometiendo un comportamiento más predecible en las distintas versiones de React. La contrapartida es una carga mental ligeramente mayor: los desarrolladores deben pensar en la estabilidad de las dos funciones y evitar devolver objetos nuevos en cada llamada.
Qué observar a continuación
Las próximas versiones de React podrían reforzar el contrato en torno a useSyncExternalStore, posiblemente añadiendo helpers integrados para las APIs de navegador más comunes. Esté atento al blog oficial de React para cualquier depreciación del antiguo patrón que imitaba a useEffect. Mientras tanto, el hook es estable y forma parte de la API pública, por lo que puede refactorizar con seguridad el código de estado externo existente.
Prueba rápida
Una demo en vivo que muestra la sincronización del tamaño de la ventana, la detección de conexión online/offline y un store simple de Zustand se encuentra en https://usesyncexternalstore.vercel.app/. El código fuente, con comentarios incluidos, está disponible en https://github.com/dev48v/usesyncexternalstore.
Idea clave: useSyncExternalStore le proporciona a React un puente fiable hacia cualquier dato mutable que resida fuera de su ciclo de renderizado, evitando el tearing y los renderizados innecesarios. Aplíquelo a fuentes externas, mantenga el snapshot mínimo y estable, y deje que React se encargue del trabajo pesado de la consistencia.
