Curso react nivel medio

Introducción
El hook useSyncExternalStore fue introducido en React 18 como una solución estandarizada para suscribirse a almacenes de estado externos (external stores) en aplicaciones React. Es útil para trabajar con bibliotecas como Redux, Zustand o cualquier sistema de estado que gestione el estado fuera del ciclo de vida de React.

Este hook asegura que React mantenga sincronizado su estado con el estado externo en aplicaciones tanto en Cliente como en Servidor.


¿Qué es useSyncExternalStore?

useSyncExternalStore te permite:

  1. Suscribirte a un estado externo para recibir actualizaciones.
  2. Garantizar que el estado se sincroniza correctamente entre renderizados.
  3. Proveer un estado inicial para renderizado en servidor (SSR).

Sintaxis

1const state = useSyncExternalStore(suscribe, getSnapshot, getServerSnapshot);
  • suscribe: Una función que se llama para suscribirse a cambios en el estado externo. Debe devolver una función para desuscribirse.
  • getSnapshot: Devuelve el valor actual del estado externo.
  • getServerSnapshot (opcional): Devuelve el estado inicial para renderizado en servidor.

Ejemplo Básico: Contador con Estado Externo

Imagina un contador gestionado fuera de React.

Almacén Externo

1// counterStore.js
2let count = 0;
3let listeners = [];
4
5export const increment = () => {
6  count += 1;
7  listeners.forEach((listener) => listener());
8};
9
10export const subscribe = (listener) => {
11  listeners.push(listener);
12  return () => {
13    listeners = listeners.filter((l) => l !== listener);
14  };
15};
16
17export const getCount = () => count;

Componente React

1import React from "react";
2import { useSyncExternalStore } from "react";
3import { subscribe, getCount, increment } from "./counterStore";
4
5function Contador() {
6  const count = useSyncExternalStore(subscribe, getCount);
7
8  return (
9    <div>
10      <h1>Contador: {count}</h1>
11      <button onClick={increment}>Incrementar</button>
12    </div>
13  );
14}
15
16export default Contador;

Explicación

  1. subscribe: React se suscribe al almacén externo (counterStore).
  2. getCount: Devuelve el valor actual del contador.
  3. Sincronización: El componente se renderiza automáticamente cuando el contador cambia.

Ejemplo con Renderizado en Servidor (SSR)

Cuando renderizas React en servidor, necesitas pasar un estado inicial para evitar inconsistencias.

Almacén Externo con Estado Inicial

1// themeStore.js
2let theme = "light";
3let listeners = [];
4
5export const setTheme = (newTheme) => {
6  theme = newTheme;
7  listeners.forEach((listener) => listener());
8};
9
10export const subscribe = (listener) => {
11  listeners.push(listener);
12  return () => {
13    listeners = listeners.filter((l) => l !== listener);
14  };
15};
16
17export const getTheme = () => theme;
18
19// Para SSR
20export const getServerTheme = () => "light"; // Estado inicial en servidor

Componente React

1import React from "react";
2import { useSyncExternalStore } from "react";
3import { subscribe, getTheme, getServerTheme, setTheme } from "./themeStore";
4
5function Tema() {
6  const theme = useSyncExternalStore(subscribe, getTheme, getServerTheme);
7
8  return (
9    <div>
10      <h1>Tema Actual: {theme}</h1>
11      <button onClick={() => setTheme("light")}>Claro</button>
12      <button onClick={() => setTheme("dark")}>Oscuro</button>
13    </div>
14  );
15}
16
17export default Tema;

Explicación

  1. getServerTheme: Proporciona un valor inicial del tema para renderizado en servidor.
  2. Sincronización Universal: React sincroniza correctamente el tema entre cliente y servidor.

Diferencias con useState y useReducer

CaracterísticauseSyncExternalStoreuseState / useReducer
Fuente del EstadoExterno (almacén global o biblioteca)Interno al componente
SincronizaciónGarantiza sincronización con ReactNo aplica, el estado es propio
Renderizado en ServidorCompatible con SSRNo diseñado específicamente para SSR

Comparación con useEffect

Aunque podrías suscribirte a un almacén externo con useEffect, useSyncExternalStore es más seguro y eficiente.

Con useEffect

1import React, { useState, useEffect } from "react";
2import { subscribe, getCount } from "./counterStore";
3
4function Contador() {
5  const [count, setCount] = useState(getCount());
6
7  useEffect(() => {
8    const unsubscribe = subscribe(() => {
9      setCount(getCount());
10    });
11    return unsubscribe;
12  }, []);
13
14  return (
15    <div>
16      <h1>Contador: {count}</h1>
17    </div>
18  );
19}
20
21export default Contador;

Con useSyncExternalStore

1import React from "react";
2import { useSyncExternalStore } from "react";
3import { subscribe, getCount } from "./counterStore";
4
5function Contador() {
6  const count = useSyncExternalStore(subscribe, getCount);
7
8  return (
9    <div>
10      <h1>Contador: {count}</h1>
11    </div>
12  );
13}
14
15export default Contador;

Diferencias

AspectouseEffectuseSyncExternalStore
Código más limpioNo
Renderizado en Servidor (SSR)No soportadoSoportado
EficienciaMenos eficienteMás eficiente

Ventajas de useSyncExternalStore

  1. Integración estándar:

    • Trabaja perfectamente con React Concurrent Mode y SSR.
  2. Simplifica código:

    • Proporciona una solución directa para manejar suscripciones sin usar useEffect.
  3. Evita Renderizados Innecesarios:

    • Solo actualiza el componente cuando el estado externo cambia.

Limitaciones

  1. Solo para Estados Externos:

    • No reemplaza useState o useReducer para manejar estado interno del componente.
  2. Mayor Configuración Inicial:

    • Requiere definir funciones como subscribe y getSnapshot.
  3. Complejidad para Nuevos Desarrolladores:

    • Puede ser más difícil de entender en comparación con useState o useEffect.

Conclusión

useSyncExternalStore es una herramienta poderosa para sincronizar React con sistemas de estado externos, ofreciendo soporte para SSR y Concurrent Mode. Es ideal para casos como:

  • Trabajar con bibliotecas de estado externo (Redux, Zustand).
  • Sincronizar componentes React con estados globales fuera del ciclo de vida de React.