Curso react nivel medio
13. Gestores de Estado Externos (Redux y Contexto Avanzado)
Para aplicaciones grandes donde se necesita un estado global más allá del Context API, Redux es una opción popular. Redux organiza el estado de la aplicación en un solo lugar llamado "store", lo que facilita el manejo y sincronización de datos entre múltiples componentes.
1. Introducción a Redux
Redux es una biblioteca para manejar el estado global de una aplicación, basada en tres principios:
- Store única: Todo el estado de la aplicación se guarda en un solo lugar, el "store".
- Estado inmutable: El estado no se modifica directamente; en su lugar, se crean copias actualizadas a través de funciones llamadas "reducers".
- Flujo de datos unidireccional: Las acciones actualizan el estado y provocan que los componentes se vuelvan a renderizar.
Redux maneja el estado mediante tres conceptos clave:
- Actions: Son objetos que describen una acción específica (por ejemplo,
{ type: 'AGREGAR_PRODUCTO', producto: { id: 1, nombre: 'Producto A' } }). - Reducers: Son funciones que reciben el estado actual y una acción, y devuelven el nuevo estado.
- Store: Es el almacén único donde se guarda el estado global de la aplicación.
2. Configuración y uso de Redux en una aplicación
Para comenzar a usar Redux, necesitamos instalarlo junto con la integración para React, llamada react-redux:
1npm install redux react-redux
Después, configuramos el store y creamos un reducer para manejar el estado.
Ejemplo básico de configuración de Redux:
1import { createStore } from 'redux'; 2import { Provider } from 'react-redux'; 3 4// Reducer inicial 5const initialState = { contador: 0 }; 6 7function contadorReducer(state = initialState, action) { 8 switch (action.type) { 9 case 'INCREMENTAR': 10 return { ...state, contador: state.contador + 1 }; 11 default: 12 return state; 13 } 14} 15 16// Crear el store con el reducer 17const store = createStore(contadorReducer); 18 19function App() { 20 return ( 21 <Provider store={store}> 22 {/* Aquí irán los componentes */} 23 </Provider> 24 ); 25} 26 27export default App;
En este ejemplo:
createStorecrea el store global usandocontadorReducercomo el manejador de estado.Providerenvuelve la aplicación, dando acceso al store a todos los componentes.
3. Redux Toolkit y Contexto avanzado
Redux Toolkit es la forma recomendada de usar Redux hoy en día, ya que simplifica la configuración y uso de Redux. Incluye herramientas para escribir reducers y acciones de manera concisa.
Para usar Redux Toolkit:
1npm install @reduxjs/toolkit
Ejemplo de configuración con Redux Toolkit:
1import { configureStore, createSlice } from '@reduxjs/toolkit'; 2import { Provider, useDispatch, useSelector } from 'react-redux'; 3 4// Crear un slice 5const contadorSlice = createSlice({ 6 name: 'contador', 7 initialState: { contador: 0 }, 8 reducers: { 9 incrementar: (state) => { state.contador += 1; }, 10 decrementar: (state) => { state.contador -= 1; }, 11 }, 12}); 13 14export const { incrementar, decrementar } = contadorSlice.actions; 15 16// Crear el store con configureStore 17const store = configureStore({ 18 reducer: { 19 contador: contadorSlice.reducer, 20 }, 21}); 22 23// Componentes usando dispatch y selector 24function Contador() { 25 const dispatch = useDispatch(); 26 const contador = useSelector((state) => state.contador.contador); 27 28 return ( 29 <div> 30 <p>Contador: {contador}</p> 31 <button onClick={() => dispatch(incrementar())}>Incrementar</button> 32 <button onClick={() => dispatch(decrementar())}>Decrementar</button> 33 </div> 34 ); 35} 36 37function App() { 38 return ( 39 <Provider store={store}> 40 <Contador /> 41 </Provider> 42 ); 43} 44 45export default App;
En este ejemplo:
createSlicecrea automáticamente el reducer, las acciones y el estado inicial.configureStoreconfigura el store sin necesidad de middleware adicional.useDispatchenvía acciones al store, yuseSelectorobtiene el estado actual del store.
4. Ejercicio práctico: Gestión de una lista de productos con Redux
Creamos una aplicación de lista de productos donde podemos agregar y eliminar productos usando Redux Toolkit.
Configuración del slice de productos (productosSlice.js):
1import { createSlice } from '@reduxjs/toolkit'; 2 3const productosSlice = createSlice({ 4 name: 'productos', 5 initialState: [], 6 reducers: { 7 agregarProducto: (state, action) => { 8 state.push(action.payload); 9 }, 10 eliminarProducto: (state, action) => { 11 return state.filter((producto) => producto.id !== action.payload); 12 }, 13 }, 14}); 15 16export const { agregarProducto, eliminarProducto } = productosSlice.actions; 17export default productosSlice.reducer;
Configuración del store (store.js):
1import { configureStore } from '@reduxjs/toolkit'; 2import productosReducer from './productosSlice'; 3 4const store = configureStore({ 5 reducer: { 6 productos: productosReducer, 7 }, 8}); 9 10export default store;
Componente de lista de productos (ListaProductos.js):
1import React, { useState } from 'react'; 2import { useDispatch, useSelector } from 'react-redux'; 3import { agregarProducto, eliminarProducto } from './productosSlice'; 4 5function ListaProductos() { 6 const [nombre, setNombre] = useState(''); 7 const productos = useSelector((state) => state.productos); 8 const dispatch = useDispatch(); 9 10 const manejarAgregarProducto = () => { 11 const nuevoProducto = { id: Date.now(), nombre }; 12 dispatch(agregarProducto(nuevoProducto)); 13 setNombre(''); 14 }; 15 16 return ( 17 <div> 18 <h1>Lista de Productos</h1> 19 <input 20 type="text" 21 value={nombre} 22 onChange={(e) => setNombre(e.target.value)} 23 placeholder="Nombre del producto" 24 /> 25 <button onClick={manejarAgregarProducto}>Agregar Producto</button> 26 27 <ul> 28 {productos.map((producto) => ( 29 <li key={producto.id}> 30 {producto.nombre} 31 <button onClick={() => dispatch(eliminarProducto(producto.id))}>Eliminar</button> 32 </li> 33 ))} 34 </ul> 35 </div> 36 ); 37} 38 39export default ListaProductos;
Integración en la aplicación principal (App.js):
1import React from 'react'; 2import { Provider } from 'react-redux'; 3import store from './store'; 4import ListaProductos from './ListaProductos'; 5 6function App() { 7 return ( 8 <Provider store={store}> 9 <ListaProductos /> 10 </Provider> 11 ); 12} 13 14export default App;
En este ejercicio:
- Slice de productos: Creamos un slice que contiene el estado y las acciones
agregarProductoyeliminarProducto. - Componente ListaProductos:
- Usa
useDispatchpara enviar acciones de agregar y eliminar productos. - Usa
useSelectorpara obtener la lista actual de productos.
- Usa
- Store global: Almacena el slice de productos, haciendo que la lista de productos esté disponible globalmente.
Con esta configuración, tenemos una lista de productos completamente funcional gestionada por Redux, lo que permite una mayor escalabilidad y organización del estado global en la aplicación.
- Loading...