¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
Conoce nuestra marca.¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
Conoce nuestra marca.dev
Eva Rodríguez 17/10/2024 Cargando comentarios…
Desde la introducción de React Hooks, el abanico de opciones para gestionar el estado de nuestras aplicaciones se ha ampliado significativamente. Entre estas opciones, destaca Redux, uno de los gestores de estado global más conocidos y utilizados. Desde sus inicios, Redux ha evolucionado de manera muy positiva, proporcionando a todos los componentes de React una única fuente de verdad a la que pueden acceder y modificar según las necesidades del proyecto.
En este artículo, nos centraremos en la implementación de Redux utilizando Redux Toolkit. Esta biblioteca oficial simplifica aún más el flujo de Redux, dejando atrás los días de configuraciones complicadas y código repetitivo.
Sabemos que existen varias formas de gestionar el estado en React (estado local, contextos, useReducer…) y Redux es especialmente útil en proyectos de gran envergadura, donde la comunicación entre distintos nodos se vuelve crucial. Aunque, en este post, veremos un ejemplo práctico en una aplicación sencilla para que sea más entendible.
¡Comenzamos!
Antes de comenzar debemos instalar las librerías react-redux y redux-toolkit a través del terminal de la siguiente manera:
npm install @reduxjs/toolkit react-redux
El store es el "almacén" del estado global, ahí guardaremos los datos que queremos que estén disponibles a nivel global. Este objeto contiene el estado actual de la aplicación y proporciona métodos para despachar acciones y suscribirse a cambios en el estado.
Crearemos el store Redux usando configureStore de Redux Toolkit. Este proceso centraliza el estado y la lógica de la aplicación en un único objeto de store. Debemos importar configureStore y pasarle un objeto de configuración con una propiedad 'reducer' que combine los reducers de los slices (porciones del estado) que crearemos más adelante
Otra de las ventajas que tiene configureStore es que está integrado con las DevTools mediante Redux DevTools Extension, facilitando la depuración y monitoreo del estado.
De momento vamos a crear el store vacío, que tendrá este aspecto:
// src/store/index.js
import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {
//aquí indicaremos más adelante los reducers de los slices
},
})
Para que el store esté disponible en toda la aplicación, debemos ir al archivo principal de nuestra aplicación React, generalmente llamado index.js o index.tsx. Este es el archivo donde se configura la raíz de la aplicación y se renderiza el componente principal App. En este archivo, importamos el componente Provider de react-redux, envolveremos el componente App con él y le pasaremos como prop el store que acabamos de crear. Esto hace que el store esté disponible para todos los componentes descendientes de App.
Es importante que el Provider envuelva a la aplicación en el nivel más alto posible. De esta manera, todos los componentes dentro del
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { store } from './store'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
)
Un slice es una función que representa una porción del estado (store) y la lógica relacionada (reducers y acciones). Para crearlo utilizaremos la función createSlice() de Redux Toolkit (RTK), en ella definimos un nombre para la slice, un estado inicial de esa porción del estado global, y un conjunto de reducers. Cada reducer define cómo cambia el estado en respuesta a una acción específica. Redux Toolkit genera automáticamente las acciones correspondientes a cada reducer.
Vamos a utilizar como ejemplo una aplicación que muestra un listado de notas, de momento, queremos guardar en nuestro slice el array de objetos que contiene las notas que vamos a mostrar (noteList). Creamos src/store/notes/notes-slice.js que tendrá el siguiente aspecto:
// src/store/notes/notes-slice.js
import { createSlice } from '@reduxjs/toolkit'
export const noteSlice = createSlice({
name: 'noteSlice', // indicamos el nombre del slice
initialState: { //aquí se guarda el estado inicial
noteList: [],
},
reducers: { //objeto que contiene las acciones que modificarán el estado
setNoteList: (state, action) => {
state.noteList = action.payload
},
addNote: (state, action) => {
state.noteList.push(action.payload)
},
},
})
export const { setNoteList, addNote } = noteSlice.actions
export const notesReducer = noteSlice.reducer
He añadido dos reducers que más adelante veremos en detalle:
Es importante exportar las acciones, generadas automáticamente, y el reducer para poder utilizarlo desde otras partes de la aplicación.
En el archivo de configuración del store, en la clave ‘reducer’ incluimos el reducer del slice que acabamos de crear (notesReducer) y lo asignamos a una clave identificativa del mismo, en este caso lo he llamado notesSliceStore, pero se podría utilizar cualquier otro nombre:
// src/store/index.js
import { configureStore } from '@reduxjs/toolkit'
import { notesReducer } from 'store/notes/notes-slice'
export const store = configureStore({
reducer: {
notesSliceStore: notesReducer,
},
})
Hasta aquí la configuración del Store, ya lo tenemos disponible para acceder a él, leerlo y modificar los datos que contiene. ¿Y cómo hacemos esto? Gracias a dos hooks proporcionados por react-redux:
Este hook permite a los componentes funcionales de React despachar acciones al store de Redux. Despachar una acción significa enviar una acción al store de Redux para que pueda ser procesada por los reducers, los cuales actualizarán el estado en base a la información que lleve esa acción (action.payload).
El action.payload es la parte de la acción que lleva los datos específicos relacionados con la acción. Es decir, el payload contiene la información necesaria para llevar a cabo el cambio de estado.
Con dos ejemplos se va a ver más claro. Volviendo a los dos reducers que hemos creado con anterioridad setNoteList y addNote, vamos a hacer un dispatch de setNoteList para que cargue en el estado la respuesta de la consulta a la API, esta consulta la hacemos desde un hook personalizado, pero se podría hacer desde cualquier otro lugar de la aplicación:
// src/hooks/useFetchAllNotes.js
import { useState, useEffect } from 'react'
import { useDispatch } from 'react-redux' //importamos useDispatch
import { setNoteList } from 'store/notes/notes-slice'
import { fetchAllNotes } from 'api/note'
export const useFetchAllNotes = () => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const dispatch = useDispatch() //almacenamos useDispatch() en una constante para poder utilizarlo en el componente
useEffect(() => {
const getAllNotes = async () => {
try {
const response = await fetchAllNotes() // obtenemos la respuesta a la consulta de la API
dispatch(setNoteList(response)) // enviamos 'response' (lo que será el action.payload) al reductor setNoteList que se encargará de cargarla en state.noteList
} catch (err) {
setError(err)
} finally {
setLoading(false)
}
}
getAllNotes()
}, [])
return { loading, error }
}
Y también hacemos un dispatch de addNote, este añadirá una nota nueva (createNote) al array noteList del estado:
// src/pages/NoteCreate/NoteCreate.jsx
import { useDispatch } from 'react-redux' //importamos useDispatch
import NoteForm from 'components/NoteForm/NoteForm'
import { addNote } from 'store/notes/notes-slice'
import { createNote } from 'api/note'
const NoteCreate = () => {
const dispatch = useDispatch() //almacenamos useDispatch() en una constante para poder utilizarlo en el componente
const handleSubmit = async formValues => {
const createdNote = await createNote(formValues)//creamos una nota nueva
dispatch(addNote(createdNote)) //enviamos la nueva nota (createNote, que será el action.payload) al reductor addNote que se encargará de añadirla al array state.noteList
}
return <NoteForm onSubmit={handleSubmit} />
}
export default NoteCreate
Recordemos que los dispatch (setNoteList y addNote) conectan con el slice del store y realizan la acción indicada:
Este hook permite a los componentes de React leer datos del store de Redux. Proporciona una forma de suscribirse a cambios en el estado y actualizar dinámicamente el componente cuando esos cambios ocurren. Utiliza la comparación de referencias para evitar renderizaciones innecesarias, lo que mejora el rendimiento de la aplicación.
Siguiendo con el ejemplo anterior, si queremos recuperar el listado de notas del estado para posteriormente realizar un .map y renderizar cada nota, haremos lo siguiente:
// src/pages/NoteList/NoteList.jsx
import { useSelector } from 'react-redux' //importamos useSelector
import Note from 'components/Note/Note'
const NoteList = () => {
const noteList = useSelector(store => store.notesSliceStore.noteList) //recuperamos de notesSliceStore (src/store/index.jsx) el reducer notesReducer y lo guardamos en la constante
return (
<ul>
{noteList.map(note => (
<li key={note.id}>
<Note title={note.title} content={note.content} />
</li>
))}
</ul>
)
}
export default NoteList
Como hemos podido comprobar, la combinación de slices de Redux Toolkit junto con useSelector y useDispatch proporciona una forma eficiente y efectiva de gestionar el estado global en aplicaciones React.
Al organizar las porciones de estado y sus reducers en un solo lugar, los slices fomentan la modularidad del código, lo que simplifica su mantenimiento y mejora su legibilidad. El uso de useSelector y useDispatch también contribuye a reducir el boilerplate, eliminando la necesidad de configuraciones complejas para conectar componentes a Redux.
Esta práctica simplifica el código, mejora el rendimiento y facilita la comprensión del flujo de datos en la aplicación.
¡Te invito a que la pongas en práctica!
Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.
Cuéntanos qué te parece.