dispatch y subscribe
El store de Redux expone una API diminuta de tres funciones —getState, dispatch y subscribe— y de su interacción nace todo el ciclo reactivo. Esta lección recorre ese ciclo paso a paso: dispatch como el único disparador que ejecuta el reducer de forma síncrona y guarda el nuevo estado; subscribe como el mecanismo por el que una vista se entera de que algo cambió; y la sutileza de que los listeners no reciben nada y deben releer con getState. Termina en el puente a la UI de 2026: cómo react-redux usa useSyncExternalStore por debajo y por qué useSelector con su comprobación de igualdad evita re-renders inútiles.
Después de las acciones y los reducers, falta la pieza que los pone en movimiento: el store, y en concreto su ciclo de vida. La sorpresa al mirarlo de cerca es lo pequeño que es. Todo el poder de Redux se ejerce a través de tres funciones —getState, dispatch y subscribe— y de la coreografía exacta entre ellas. dispatch es el único gesto capaz de cambiar el estado; subscribe es la única forma de enterarse de que cambió. Entre las dos dibujan un bucle síncrono y de un solo sentido que es, literalmente, el latido de la aplicación. Entender ese latido —qué ocurre, en qué orden, y de forma síncrona— es entender cómo una vista al fondo de la jerarquía se entera de un cambio que ocurrió en cualquier parte.
- Conocer la API mínima del store:
getState,dispatchysubscribe. - Seguir el ciclo síncrono de
dispatch: ejecutar el reducer, guardar, notificar. - Entender cómo una vista se entera de un cambio y por qué relee con
getState. - Situar el puente a la UI en 2026:
useSyncExternalStoreyuseSelector.
Tres funciones y nada más
El objeto que devuelve createStore no es una clase con docenas de métodos: es un puñado de funciones cerradas sobre un estado privado. getState devuelve el árbol de estado actual —la única lectura—. dispatch recibe una acción y dispara el cambio —la única escritura—. subscribe registra un callback que se ejecutará tras cada cambio, y devuelve una función para cancelar esa suscripción. Con esas tres piezas se construye todo lo demás, incluido react-redux.
const store = createStore(reducer)
const estado = store.getState() // leer el arbol actual
const cancelar = store.subscribe(() => { // registrar un oyente
console.log('algo cambio:', store.getState())
})
store.dispatch({ type: 'contador/incrementado' }) // disparar un cambio
cancelar() // dejar de escuchar
No existe store.state como propiedad pública que puedas leer o asignar. El estado vive encerrado dentro del store, y las tres funciones son las únicas puertas: una para mirar (getState), una para cambiar (dispatch), una para escuchar (subscribe). Esta encapsulación es lo que hace cumplible el principio de solo lectura del que hablamos en la primera lección: no puedes saltarte dispatch porque, sencillamente, no hay ninguna otra manija que girar.
dispatch: el único disparador
dispatch(accion) es el corazón del ciclo, y lo primero que sorprende es que es síncrono por defecto. Cuando lo llamas, ocurren cuatro cosas en orden inmediato, sin esperas ni microtareas: el store toma el estado actual y la acción, llama al reducer con ambos, guarda como nuevo estado lo que el reducer devuelve, y ejecuta uno por uno todos los listeners registrados con subscribe. Cuando dispatch retorna, todo eso ya pasó: el estado ya es el nuevo y los oyentes ya fueron avisados.
flowchart TD D[dispatch accion] --> R[reducer estado mas accion] R --> N[store guarda nuevo estado] N --> L[notifica a todos los listeners] L --> G[cada listener llama getState y relee] G --> V[la vista se recalcula] style D fill:#f9e2af,color:#11111b style R fill:#89b4fa,color:#11111b style N fill:#cba6f7,color:#11111b style V fill:#a6e3a1,color:#11111b
Que sea síncrono tiene una consecuencia importante: dentro del propio reducer no puedes despachar otra acción, y despachar durante la notificación de un listener es un antipatrón que puede provocar bucles. El modelo mental correcto es que dispatch es una transacción atómica sobre el estado: entra una acción, sale un estado nuevo y consistente, y solo entonces se avisa al mundo. Lo asíncrono —esperar una respuesta de red antes de despachar el resultado— no vive aquí, sino en el middleware que se interpone antes de que la acción llegue al reducer, tema del nivel 20.
subscribe: cómo una vista se entera
Aquí se responde la pregunta que da título a la lección. Una vista no sabe por arte de magia que el estado cambió; se entera porque se suscribió. subscribe(listener) mete el listener en la lista de oyentes del store; a partir de ese momento, cada dispatch lo invoca. El detalle crucial, y el que más confunde al principio, es que el listener no recibe ningún argumento: no le pasan ni el estado nuevo ni la acción que ocurrió. Su único trabajo es reaccionar releyendo, y para eso vuelve a llamar a getState.
Ese diseño es deliberado y elegante. Al no decirle al listener qué cambió, Redux lo obliga a tratar cada notificación igual —“algo pasó, vuelvo a leer lo que me interesa y me recalculo”— en lugar de intentar procesar diffs. La notificación es una señal de tipo push —te avisan de que hubo un cambio—, pero la lectura es de tipo pull —tú vas a buscar el dato—, exactamente la combinación que estudiaste al hablar de observadores en el nivel 3. La vista no persigue el cambio concreto; recibe un empujón y ella misma tira del estado que necesita.
// El patron crudo que react-redux automatiza por dentro.
let anterior = selector(store.getState())
const cancelar = store.subscribe(() => {
const actual = selector(store.getState())
if (actual !== anterior) { // solo si lo que me importa cambio
anterior = actual
renderizar(actual) // recalculo la vista con el nuevo valor
}
})
Si un listener se recalcula ante cualquier dispatch, se ejecutará aunque haya cambiado una parte del estado que no le concierne. La cura es el selector con comprobación de igualdad: extraes solo el trozo que te importa, lo comparas con el valor anterior, y actúas únicamente si difiere. Es el mismo principio de los selectores y la memoización del nivel 6, aplicado al puente entre store y vista. Sin él, un store activo dispara re-renders inútiles por toda la app.
El puente a la UI en 2026
Casi nadie llama a subscribe a mano: lo hace la librería de integración con la UI. En React eso es react-redux, y su historia interna es instructiva. Durante años implementó su propia lógica de suscripción, comparación y re-render sobre subscribe y getState. Desde React 18, delega en useSyncExternalStore, el hook que React introdujo precisamente para conectar stores externos de forma segura frente al renderizado concurrente, evitando el tearing —que dos partes de la pantalla lean versiones distintas del mismo estado en el mismo render—. En la práctica, useSelector que usas en 2026 se apoya en ese primitivo.
import { useSelector, useDispatch } from 'react-redux'
function Contador() {
const valor = useSelector((estado) => estado.contador.valor) // subscribe + getState + igualdad
const dispatch = useDispatch()
return <button onClick={() => dispatch({ type: 'contador/incrementado' })}>{valor}</button>
}
useSelector encapsula el ciclo entero: suscribe el componente al store, ejecuta tu selector tras cada dispatch, compara el resultado con el anterior y solo provoca un re-render si cambió. useDispatch te da la única puerta de escritura. Toda la coreografía de getState, dispatch y subscribe que has visto sigue ahí debajo, intacta; la librería solo la esconde tras dos hooks para que no la tejas a mano en cada componente. Esto conecta con lo que ya trabajaste al enviar eventos desde la UI y optimizar con useSelector en el nivel 15.
Si abstraes lo suficiente, Redux es un bucle de eventos minúsculo y perfectamente ordenado. Las acciones son los eventos que entran en la cola; dispatch es el único punto de entrada a esa cola; el reducer es el manejador que procesa cada evento produciendo el siguiente estado; y subscribe es la lista de observadores que se despiertan cuando el estado avanza. Visto así, desaparece la aparente magia de la reactividad: no hay hilos ocultos ni observación automática de propiedades como en los signals del nivel 4, hay un ciclo explícito y síncrono que cualquiera puede seguir con el dedo. Esa transparencia es justamente el intercambio que Redux propone frente a la reactividad automática. Un signal se actualiza solo y no ves el cable; un store de Redux te obliga a despachar y a suscribirte, pero a cambio cada cambio deja un rastro nombrado —qué acción, en qué orden, produjo qué estado— que puedes registrar, inspeccionar y reproducir. La combinación push para avisar y pull para leer es lo que mantiene ese rastro limpio: el store no empuja datos hacia las vistas arriesgándose a inconsistencias, sino que empuja una señal de cambio y deja que cada vista vaya a buscar, en el mismo instante y desde la misma fuente única, exactamente lo que necesita. Un solo actor, una sola cola, un solo estado autoritativo: esa modestia deliberada es la razón de que Redux sea tan depurable como aburrido, y en infraestructura de estado el aburrimiento es el mayor de los elogios.
- Crea un store, suscríbele un listener que imprima
getState, despacha tres acciones y observa el orden síncrono: reducer, guardado, notificación. - Comprueba que el listener no recibe argumentos y que la única forma de saber qué hay ahora es volver a llamar a
getState. - Añade un selector con comparación de igualdad y verifica que el listener solo actúa cuando cambia el trozo que le importa, no ante cualquier
dispatch. - Guarda la función que devuelve
subscribey cancela la suscripción a mitad; confirma que a partir de ahí el listener deja de dispararse. - Sustituye todo tu cableado manual por
useSelectoryuseDispatchde react-redux y razona qué parte del ciclo esconde cada hook.