cover

Introducción a Redux-Toolkit

author photo

Héctorbliss

@hectorbliss

Mira el video si prefieres:

Redux-Toolkit es la forma más moderna de trabajar con Redux hoy en día. Además de que ahora es más fácil implementar Redux que antes.

Por eso, y para refrescarnos la memoria, si ya has trabajado con versiones anteriores de Redux, vamos a aprender cómo utilizar Redux-Toolkit en esta pequeña serie de 2 entradas. Comencemos con instalarlo todo y aprendamos a usarlo fácilmente. ⚡️

😎 Creando el nuevo proyecto e Instalando dependencias

Vamos a crear un nuevo proyecto React con Vite para poder instalar Redux-Toolkit y configurar todo desde cero.

npx create-vite cd rtk

Llamaremos al proyecto: rtk, seleccionaremos React y el lenguaje de tu preferencia, yo seleccionaré TypeScript. Ahora instalemos las dos herramientas que necesitamos para implementar Redux en este nuevo proyecto.

npm i @reduxjs/toolkit react-redux && npm i

De paso, instalamos todas las dependencias.

📦 Features folder y tu primer slice

Abrimos nuestro proyecto en tu editor de código favorito y creamos una carpeta llamada features dentro de la carpeta src. Para luego crear nuestro primer archivo de Redux: counter-slice.ts. Para este ejemplo, vamos a reutilizar el componente que Vite ya nos regala y que tiene un counter, por eso vamos a crear un “counter-slice”.

// src/features/counter-slice.ts // importamos createSlice para crear nuestro primer slice import { createSlice, PayloadAction } from "@reduxjs/toolkit"; // Declaramos el tipo de nuestra data, en este caso solo necesitamos un valor númerico. type CounterState = { value: number; }; // Declaramos un estado inicial, de preferencia. const initialState: CounterState = { value: 0, }; // Aquí está la mágia, este es nuestro slice. const counterSlice = createSlice({ name: "counter", // El nombre de nuesto slice initialState, // El estado inicial reducers: { // Todos los reducers, que también se convertirán en acciones increment(state) { state.value++; }, }, }); export const { increment } = counterSlice.actions; // Exportamos los métodos de las acciones // Y exportamos el reducer como default de este archivo. export default counterSlice.reducer;

En este tutorial, lo haremos todo con tipado seguro utilizando TypeScript, para lograr un end-to-end type safety. Por eso hemos importado PayloadAction también (lo usaremos más adelante). ✅

⛺️ Ahora vamos a configurar el store

Una vez que tenemos nuestro primer slice: podemos configurar el store, pues el único requisito para crearlo, es justamente, tener un primer reducer. Así que vamos a crear un archivo para nuestra configuración del store en: src/store.ts.

// Importamos la herramienta para crear el store y nuestro primer reducer. import { configureStore } from "@reduxjs/toolkit"; import counterReducer from "./features/counter-slice"; // Creamos el store con configureStore. const store = configureStore({ reducer: { counter: counterReducer }, // Ahora los reducers se combinan automáticamente. }); // Explotaremos el tipado que Redux Toolkit nos regala. export type AppDispatch = typeof store.dispatch; export type RootState = ReturnType<typeof store.getState>; // Exportamos store como default (o como prefieras) export default store;

Una vez más, declaramos tipos y los exportamos para usarlos más adelante.

🔌 Es momento de conectarlo todo

Vamos a nuestro archivo principal main.tsx, donde usamos ReactDOM y agregamos el <Provider> de react-redux; proporcionamos el store.

import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; // Importamos tanto el provider como el store import { Provider } from "react-redux"; import store from "./store"; ReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode> );

🔬 Estamos listos para probar con las Redux Dev Tools

Para hacer la prueba de nuestra configuración, vamos a instalar las herramientas de desarrollo de Redux. Una forma simple de hacerlo es instalar el plugin para el navegador Chrome.

Una vez instalado, abriremos el inspector (chrome dev tools) y ejecutamos nuestra aplicación con npm run dev.

redux dev tools

Podremos ver que nuestro estado counter ¡está listo! 🤩

🛸 Es hora de usar los Hooks de RTK

Hemos visto cuál es la forma moderna de configurar Redux en nuestro proyecto; es momento de utilizar los Hooks que RTK nos regala: useDispatch y useSelector, pero vamos a tiparlos correctamente para sacarle el máximo provecho a TypeScript. 🤓

// src/hooks.ts // Importamos los conocidos useDispatch y useSelector, además de un tipo import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import { RootState, AppDispatch } from "./store"; // Observa cómo estamos re-declarando los hooks, incluyendo los tipos en los generics, esto generará en automático los nuevos hooks tipados 🤯 export const useAppDispatch = () => useDispatch<AppDispatch>(); export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

Hemos escrito esta configuración para nuestros Hooks en un archivo llamado hooks.ts dentro de src. Pareciera que este paso está de más, pero no es así, pues estás a punto de presenciar la magia 🎩 de Redux + TypeSciript.

🪄 Por fin, usaremos Redux en nuestro componente <App>

Ahora sí, todo está en su lugar, incluida la magia, 🪄 así que vamos a utilizar nuestro estado global para sustituir el contador local que ha creado Vite en el archivo App.tsx.

import reactLogo from "./assets/react.svg"; import viteLogo from "/vite.svg"; import "./App.css"; // Importamos los Hooks que ha generado Redux por nosotros (estos hooks incluyen los tipos) import { useAppDispatch, useAppSelector } from "./hooks"; // También importamos nuestra accion import { increment } from "./features/counter-slice"; function App() { // Sustituimos el estado local por el de Redux const count = useAppSelector((state) => state.counter.value); const dispatch = useAppDispatch(); return ( <> <div> <a href="https://vitejs.dev" target="_blank"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> <a href="https://react.dev" target="_blank"> <img src={reactLogo} className="logo react" alt="React logo" /> </a> </div> <h1>Vite + React</h1> <div className="card"> {/* Usamos dispatch pasando directamente la invación de increment */} <button onClick={() => dispatch(increment())}>count is {count}</button> <p> Edit <code>src/App.tsx</code> and save to test HMR </p> </div> <p className="read-the-docs"> Click on the Vite and React logos to learn more </p> </> ); } export default App;

Si pones tu mouse encima de la constante count o de dispatch, te darás cuenta de que el tipo es el correcto, gracias a TS. 🥳

👀 Hora de viajar en el tiempo. Recuerda que con las Redux Dev Tools, puedes jugar con la linea del tiempo, rebobinando las acciones para mirar cómo va cambiando el estado.

👯‍♀️ Vamos a agregar una segunda acción

Ahora que podemos usar nuestras acciones y el estado global en nuestros componentes, vamos a agregar una acción, solo para experimentar y familiarizarnos aún más con esta forma de trabajar. Y de paso, haremos uso del payload en el reducer.

// extra action // ... const counterslice = createSlice({ name: "counter", initialState, reducers: { increment(state){ state.vale++; // Gracias a immer, esto es inmutable }, // Aquí es donde usamos el tipo pasando el generic del tipo de dato esperado addAmount(state, action: PayloadAction<number>) { state.value += action.payload; }, } }); // ... export const { incremented, addAmount } = conterSlice.actions;

Ahora que hemos exportado nuestra nueva acción, vamos a usarla en el componente <App>.

// App.tsx import { increment, addAmount } from "./features/counter-slice"; // ... const handleClick = ()=>{ dispatch(amountAded(3)); } // ... <button onClick={() => dispatch(increment())}>count is {count}</button> <br /> <input type="number" onChange={(e) => dispatch(addAmount(Number(e.target.value)))} /> // ...

Si hacemos la prueba ahora, veremos que nuestra acción se ejecuta correctamente. 🥳

¡Ya tienes todo listo para que tus aplicaciones React trabajen con Redux Toolkit!

Lo haz hecho muy bien, yo creo que ya estás listo(a) para que agreguemos asincronía y cache con Redux Query, ¿no te parece? Bueno, pues no te pierdas la siguiente entrada, en la que usaremos este proyecto nuevamente, y agregaremos Redux Query para consumir una API de servidor.

Abrazo. Bliss. 🤓

Enlaces relacionados

Redux Dev tools

meta cover

¿Qué son las Future Flags?

Checa este otro Post

meta cover

Cómo tipar eventos en React con TypeScript

Checa este otro Post

¡Nuevo curso!

Animaciones web con React + Motion 🧙🏻