Contenido del curso
Introducción a nuestro proyecto
¡Atraparlos ya!
React.js + Redux
Middlewares
Avanzando la ui
Inmutabilidad
Avanzado
Despedida del curso
combineReducers para separar data y UI en Redux
Resumen
Separar responsabilidades en Redux mejora la mantenibilidad cuando tu estado crece. Con combineReducers divides tu store en piezas pequeñas que manejan dominios distintos, como datos y UI, evitando un reducer gigante e inmanejable.
¿Por qué conviene dividir el estado en varios reducers?
Cuando tu aplicación crece, mezclar datos del dominio con flags de interfaz en un solo reducer genera archivos imposibles de mantener. Piensa en la vista de una clase de Platzi: ahí conviven tu perfil, el temario del curso, la información de la clase y los aportes de la comunidad. Meter todo eso en un único reducer rompe el principio de separación de responsabilidades.
En el proyecto de pokémons pasa algo similar. El estado tenía dos propiedades muy distintas: pokemons, que es la data que le importa al usuario, y loading, que es un control de UI para mostrar un indicador mientras una acción se ejecuta. Son cosas diferentes y merecen vivir separadas.
¿Qué hace combineReducers en Redux? Es un helper que une varios reducers pequeños en un rootReducer. Cada reducer maneja una porción del estado y combineReducers los expone como propiedades de un mismo objeto.
¿Cómo separar el reducer de data y el reducer de UI?
El primer paso es dividir el archivo original en dos. En el reducer de pokémons te quedas solo con el initialState de la lista y los casos setPokemon y setFavorite. En el reducer de UI conservas únicamente el initialState del loading y el caso setLoading, y lo renombras a uiReducer [02:30].
Luego creas un archivo nuevo llamado rootReducer donde vas a combinar ambos.
¿Por qué usar Redux Immutable en vez de Redux?
Normalmente importarías combineReducers desde redux, pero como el proyecto trabaja con Immutable JS, necesitas una librería específica que entienda esas estructuras. Por eso instalas redux-immutable y desde ahí importas el método [03:45].
El rootReducer queda así:
javascript import { combineReducers } from 'redux-immutable'; import pokemonsReducer from './pokemons'; import uiReducer from './ui';
const rootReducer = combineReducers({ data: pokemonsReducer, ui: uiReducer, });
export default rootReducer;
Cada propiedad del objeto que recibe combineReducers se convierte en una rama de tu estado. Si mañana necesitas un reducer de usuario, solo agregas otra propiedad y listo.
¿Cómo conectar el rootReducer al store?
En tu index.js le pasas el rootReducer a createStore en lugar del reducer suelto de pokémons. Al recargar el navegador y abrir Redux DevTools verás dos propiedades principales: data con la lista de pokémons y ui con el booleano loading [05:30].
¿Cómo acceder al estado cuando hay varios niveles?
Al combinar reducers, el estado gana un nivel extra de profundidad. Antes leías pokemons directo, ahora vive dentro de data. En App.js tienes que actualizar las lecturas con getIn de Immutable, indicando la ruta completa.
- Para la lista de pokémons usas
getIn(['data', 'pokemons']). - Para el loader usas
getIn(['ui', 'loading']). - También puedes encadenar
.get().get()si lo prefieres.
Con ese ajuste el loader vuelve a aparecer y la lista de pokémons carga correctamente.
¿Por qué necesitas shallowEqual con useSelector?
Aquí viene lo interesante. useSelector decide si re-renderiza tu componente usando una comparación estricta, ese === que revisa si dos valores son exactamente iguales. Para strings o booleanos compara el valor directo, pero para objetos y arreglos compara referencias, no contenido.
Como trabajas con inmutabilidad, cada cambio crea una referencia nueva aunque los valores internos sean idénticos. Resultado: re-renders innecesarios todo el tiempo.
¿Qué es shallowEqual en React Redux? Es una función que importas desde react-redux y le pasas como segundo argumento a useSelector. Compara los valores de primer nivel del objeto en lugar de la referencia, evitando renders falsos.
Lo aplicas así:
javascript import { useSelector, shallowEqual } from 'react-redux';
const pokemons = useSelector( (state) => state.getIn(['data', 'pokemons']), shallowEqual );
Para el loading no hace falta shallowEqual porque es un booleano y la comparación estricta basta. Solo lo necesitas cuando seleccionas objetos o arreglos [08:45].
¿Es obligatorio usar combineReducers?
No, es totalmente opcional. Puedes manejar todo en un solo reducer si quieres, aunque no se recomienda. combineReducers existe para mejorar la experiencia de desarrollo y aplicar el principio de dividir y vencerás sobre tu estado global.
Con esta separación entre data y UI ya tienes un reducer compuesto limpio, y en la siguiente clase verás cómo Redux Toolkit simplifica todo este flujo mediante slices. ¿Cómo organizas tú los reducers en tus proyectos? Cuéntalo en los comentarios.