Integrando Redux con connect en React

Resumen

Conectar Redux a una aplicación React puede sonar intimidante al inicio, pero cuando entiendes el flujo de actions, reducers y store, todo cobra sentido. Aquí verás cómo integrar Redux en React usando connect, mapStateToProps y mapDispatchToProps, ideal si estás migrando un componente que antes usaba useState hacia un manejo de estado global.

¿Qué librerías necesitas para empezar con Redux en React?

Antes de escribir código, instala las dos piezas base que harán posible la integración.

  • redux: el núcleo que gestiona el estado global.
  • react-redux: añade optimizaciones y reduce boilerplate al trabajar con React.

Ambas se instalan con npm install redux react-redux desde la terminal. La segunda es clave porque te da acceso a utilidades como Provider y connect, que conectan tus componentes al store.

¿Qué hace react-redux? Es la capa que une React con Redux. Ofrece el componente Provider para inyectar el store y métodos como connect para que tus componentes lean y modifiquen el estado global.

¿Cómo organizar las actions y los action creators?

La estructura que uses al inicio te ahorra muchos dolores de cabeza después. En la carpeta source crea una carpeta actions y, dentro, un archivo separado para los types.

¿Por qué separar los types? Porque escribir strings directamente en cada action y reducer es propenso a errores de tipeo. Si en un lugar escribes setPokemons y en otro setPokemon, la aplicación se rompe sin darte una pista clara del origen.

La solución es exportarlos como constantes [02:00]:

js export const SET_POKEMONS = 'SET_POKEMONS';

Luego, en index.js defines tu action creator, que es simplemente una función que retorna un objeto describiendo el cambio:

js export const setPokemons = (payload) => ({ type: SET_POKEMONS, payload, });

El payload es la información que viaja con la acción, en este caso la lista de pokémones que viene del API.

¿Qué es un action creator en Redux?

Un action creator es una función que retorna un action, es decir, un objeto plano con una propiedad type y, opcionalmente, un payload. Su único trabajo es describir qué cambio quieres ejecutar, no ejecutarlo.

¿Cómo conectar un componente a Redux con connect?

Aquí entra la magia de react-redux. Dentro de app.js importas connect y defines dos piezas por convención: mapStateToProps y mapDispatchToProps [04:30].

mapStateToProps es una función que recibe el estado global y retorna un objeto cuyas propiedades llegan como props al componente:

js const mapStateToProps = (state) => ({ pokemons: state.pokemons, });

mapDispatchToProps recibe el dispatch de Redux y retorna un objeto donde cada propiedad envuelve un action creator en una llamada al dispatcher:

js import { setPokemons as setPokemonsActions } from './actions';

const mapDispatchToProps = (dispatch) => ({ setPokemons: (value) => dispatch(setPokemonsActions(value)), });

El truco del as setPokemonsActions evita la colisión de nombres entre el action creator importado y la función que vas a exponer al componente. Finalmente, conectas todo:

js export default connect(mapStateToProps, mapDispatchToProps)(App);

¿Qué diferencia hay entre mapStateToProps y mapDispatchToProps? El primero entrega datos del estado global como props al componente. El segundo entrega funciones que disparan actions para modificar ese estado.

¿Cómo crear un reducer y configurar el store?

El reducer es una función pura que decide cómo cambia el estado según el action recibido. En source/reducers/pokemons.js defines primero el initialState como un objeto clave-valor, lo que da más flexibilidad para crecer.

js const initialState = { pokemons: [] };

export const pokemonsReducer = (state = initialState, action) => { switch (action.type) { case SET_POKEMONS: return { ...state, pokemons: action.payload }; default: return state; } };

Usar switch en vez de varios if/else mantiene el código legible. La copia con spread { ...state } aplica el principio de inmutabilidad: nunca mutas el estado anterior, devuelves uno nuevo.

¿Por qué importar legacy_createStore en lugar de createStore?

Al configurar el store en index.js, vas a notar algo curioso. Si importas createStore directamente desde Redux, aparece un warning recomendando usar Redux Toolkit. Para evitarlo mientras aprendes el método clásico, importas la versión renombrada [10:30]:

js import { legacy_createStore as createStore } from 'redux'; import { Provider } from 'react-redux'; import { pokemonsReducer } from './reducers/pokemons';

const store = createStore(pokemonsReducer);

Luego envuelves tu <App /> con <Provider store={store}> para que toda la aplicación tenga acceso al estado global. Este patrón sigue siendo común en proyectos productivos, así que vale la pena conocerlo aunque Redux Toolkit sea la recomendación oficial.

¿Cómo reemplazar useState por Redux en el componente?

Una vez conectado, el cambio en App.js es mínimo. Eliminas la línea de useState y recibes pokemons y setPokemons directamente como props:

js const App = ({ pokemons, setPokemons }) => { /* ... */ };

Las llamadas a setPokemons(data) cuando llega la respuesta del API se mantienen idénticas. La diferencia es que ahora el estado vive en el store y cualquier componente conectado puede leerlo.

Para verificar que todo funciona, un console.log(pokemons) y reiniciar el servidor te mostrarán la lista llegando correctamente. Una extensión útil para esto es Turbo Console Log, que genera el console.log automáticamente sobre la variable seleccionada.

¿Has conectado componentes a Redux con connect o prefieres los hooks useSelector y useDispatch? Cuéntame en los comentarios qué método te resulta más cómodo y por qué.