Introducción react-redux y redux toolkit
Tags: typescript, react, redux, webdev
Published: 2022-03-23 23:52:07

Después de haber entendido algunos conceptos de Redux, llegó el momento de utilizarlo como tal 😁.

## Setup

Para hacer el setup de la app usaré [vite](https://vitejs.dev/). Vite es una alternativa a webpack, el cual mejora la experiencia de usuario y es mucho más rápido. Si quieres saber más acerca de los beneficios de vite, puedes visitar el siguiente [artículo](https://radixweb.com/blog/vite-js-latest-front-end-development-tool)

Para crear una aplicación con vite y react, solo es necesario abrir tu consola y poner el siguiente comando.


```sh
yarn create vite redux-tutorial --template react-ts

```
La opción de `--template` le dice a vite con que template inicializar el proyecto, en este caso el de `react-ts` ahora ya se tendrá la siguiente estructura.


![file structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mzdukxqdot80053e27it.png)

Ahora empecemos con las dependencias, como habíamos dicho utilizaremos redux con react, para eso tenemos que instalar el paquete `react-redux`, el cual trae lo necesario para hacer la conexión a redux, además a ellos vamos a instalar `@reduxjs/toolkit` el cual trae algunos superpoderes para redux


```sh
yarn add react-redux @reduxjs/toolkit
```

## ¿Que es redux toolkit?

Redux esta bien, pero era un poco complicado. Actualmente, contamos con [Redux toolkit](https://redux-toolkit.js.org/) él ofrece las siguientes soluciones:

- Simplifica la configuración de redux
- Elimina la necesidad de agregar múltiples paquetes para tener una aplicación escalable.
- Reduce el código repetitivo.

Actualmente no se recomienda usar `react-redux` sin `@reduxjs/toolkit`.

## Preparando el Store

Para empezar a escribir lógica con redux, lo primero que se tiene que hacer es configurar el `Store`. Para eso [Redux toolkit](https://redux-toolkit.js.org/) provee un método que nos ayuda con el procedimiendo, el cual se llama `configureStore`.


```tsx
// store/index.ts

import { configureStore } from "@reduxjs/toolkit";

export const store = configureStore({
  reducer: {},
  devTools: process.env.NODE_ENV !== "production",
});

export default store;

```


[configureStore](https://redux-toolkit.js.org/api/configureStore)


Ahora ya tenemos él store :) . Al  hacer esto `redux-toolkit` ha puesto algunas configuracioens por defecto, las cuales iré comentando conforme avancemos en el ejemplo. En este instante podemos hablar de las [devTools](https://github.com/reduxjs/redux-devtools) las cuales son indispensables para poder depurar la aplicación. En este caso la opción `devtools` se activa solo en producción, también puedes personalizar el funcionamiento, pasando un objeto de [opciones](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md).

**Conexión con React**

Ahora es momento de poner disponible el store hacia React, para eso `react-redux` provee un `Provider` para poner disponible el `Store` en todo el árbol de componentes.

```tsx
import "./App.css";
import { Provider as ReduxProvider } from "react-redux";
import store from "./store";

function App() {
  return (
    <ReduxProvider store={store}>
      <div></div>
    </ReduxProvider>
  );
}

export default App;

```

## Estructura de archivos en Redux

Ahora que ya se tiene la tienda en el nivel superior de la aplicación, es hora la lógica de nuestra aplicación, en este caso vamos a hacer una agenda de contactos, para poder realizar un CRUD. Antes de continuar necesitamos tener en cuenta algo muy importante, el cual es la estructura de los archivos. Si bien React es una librería muy flexible frente a la estructura de archivos, Redux poner a nuestra disposición una estructura base, para a partir de eso organizar nuestros archivos.

**Pensando en Ducks**

Ducks es una [propuesta](https://github.com/erikras/ducks-modular-redux) que básicamente propone que empaquetemos un conjunto de acciones, reductores, nombres de acciones a una funcionalidad en concreto, llamando a esta agrupación `duck` el cual, tranquilamente, puede empaquetarse y distribuirse como una librería.

Ahora, teniendo un poco en cuenta los patos 😅, vamos a dividir la aplicación en algo parecido, pero le llamaremos `features`. De esta manera.

![Modules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hme0jdu472ze6j8vxes.png)


**Feature Structure:**

Como se mencionaba en un inicio, el ecosistema de React es muy flexible a la hora de organizar los archivos. Teniendo en cuenta los elementos de redux, al momento de partir una `feature` debemos dividir `actions`, `reducer`, `selectors` esto mejora la organización.

En mi caso inspirado un poco en el siguiente [artículo](https://alexmngn.medium.com/how-to-better-organize-your-react-applications-2fd3ea1920f1), mi estructura es la siguiente.

![modules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ab7uvy0bqnrvoelbnn5z.png)

_View_: Carpeta donde van las vistas que el usuario va a ver en pantalla, generalmente todos los componentes que son utilizando junto con el router de la aplicación. Por ejemplo, si estamos creando una aplicación de inventarios, el listado de estos productos podrían ir en una pantalla `producs/list`.

_Componentes_: Normalmente, se querrá tener una carpeta `components` en general, donde estén ubicados todos aquellos componentes, que **pueden ser usados en cualquier lugar**, una feature puede tener componentes que sean propios de la característica, por ejemplo el listado, de productos.

_actions:_ En esta carpeta irán todas las acciones ligadas a esta característica.

_reducer:_ Cada feature tiene como regla que debe exportar un solo reductor, eso no quiere decir que solo tengamos que concentrar toda lógica en un solo reducer, podemos usar [`combineReducers`](https://redux.js.org/api/combinereducers) para combinar múltiples reducer en unos solo si es que fuese necesario.


Puedes pensar en una `feature` como una mini aplicación dentro de una aplicación, este se encarga de un proceso en concreto, que al final aportará un valor agregado a la aplicación en general.

## Accciones

Las acciones son objetos planos que expresan una intención de cambiar el estado, eso es lo que se mencionó en el artículo anterior. Puedes pensar en una acción como un evento ocurrido en la aplicación, por ejemplo; se agregó un producto, se eliminó un contacto, cargando contactos, todos ellos describen algo que está pasando en al app.

Dicho esto podemos empezar a escribir acciones, las acciones tiene un [estándar](https://github.com/redux-utilities/flux-standard-action) que indican que deben ser así.

```tsx
{
  type: 'ADD_TODO',
  payload: {
    text: 'Do something.'
  }
}
```

**Creadores de acciones:**

Normalmente, las acciones puede despacharse de la siguiente manera.

```tsx
store.dispatch({ type: "ITEM_ADDED_TO_CART", payload: 47 });
```

Pero en cierto punto, poner el tipo, cada vez que queramos despachar esta acción, no es muy escalable porque si se quisiera cambiar el tipo de acción, tendría que hacerlo en diferentes archivos y además,  se vuelve complicado repetir lo mismo.


Ahí es donde entran loas [creadores de acciones](https://read.reduxbook.com/markdown/part1/04-action-creators.html), que no son nada más que funciones encargadas de crear este objeto, un creador de acción sería el siguiente.

```tsx
function doAddToDoItem(text) {
  return { type: "TODO_ADDED", payload: text };
}
```

Entonces, cada vez que se requiera formar esta acción, solo es necesario ejecutar `doAddToDoItem`.


Redux toolkit Simplifica este procedimiento con un utility llamado [`createAction`](https://redux-toolkit.js.org/api/createAction) el cual es una [HOF](https://leobar37.medium.com/hofs-clousures-y-callbacks-para-ser-feliz-en-javascript-fa105ed6ad44)(higher order function) los cuales son funciones que retornan funciones.

```ts
// features/schedule/actions/schedule.actions.ts
import { createAction } from "@reduxjs/toolkit";

export const contactAdded = createAction("CONTACT_ADDED");
```

Ahora `contactAdded` es una función que al ser disparada creará una acción de tipo `CONTACT_ADDED` es importante saber que por [recomendación de redux](https://redux.js.org/style-guide/style-guide#model-actions-as-events-not-setters) las acciones deben ser "Descripciones de eventos que ocurrieron" en lugar expresarlas en presente, como por ejemplo `ADD_CONTACT`.


**Payload:**

Hasta este paso se creó la acción `contactAdded`, pero esto no es suficiente para agregar un contacto, se necesitaría la información de ese contacto. En el caso de typescript `redux toolkit` tiene un [genérico](https://www.typescriptlang.org/docs/handbook/2/generics.html) para poder describir el payload.


```ts
import { createAction } from "@reduxjs/toolkit";

export const contactAdded =
  createAction<{ name: string; phone: string }>("CONTACT_ADDED");
```

Listo ahora el primer parámetro(payload) de `contactAdded` estará tipado.

## Reducer

Como se mencionó anteriormente, los [reducers](https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers#writing-reducers) son funciones puras que toman el estado actual y la acción para retornar un nuevo estado.

[Redux toolkit](https://redux-toolkit.js.org/api/createreducer) exporta una función llamada `createReducer` la cual facilita la creación de un reducer agregando ciertas características que facilitan el desarrollo.

```ts
import { createReducer } from "@reduxjs/toolkit";

const initalState = {
  contacts: [],
};

export type ScheduleState = typeof initalState;

const reducer = createReducer(initalState, (builder) => {});
```

Esta sería la forma de crear un reducer con **Redux toolkit**

**Case:**

Anteriormente vimos, que cuando creamos un reducer dentro de el planteamos un `switch...case` para manejar cada acción.

```ts
const reducer = (state, action) => {
  switch (action) {
    case "EAT": {
      return {
        ...state,
        eatCount: state.eatCount + 1,
      };
    }
  }
};
```
Redux toolkit propone una forma más amigable de hacerlo, mediante un objeto builder, el cual expone una serie de métodos como `addCase` con el cual le recibe como parámetros.


**ActionCreator:** La función generada por `createAction`o una acción como tal.

**Reducer:** El reducer encargado solo de manejar esta acción.

Incorporando la lógica de agregar contacto, tendríamos lo siguiente.

```ts
import { createReducer } from "@reduxjs/toolkit";
import * as scheduleActions from "../actions/schedule.actions";

export interface IContact {
  id: number;
  name: string;
  phone: string;
}
const initalState = {
  contacts: [] as IContact[],
};

export type ScheduleState = typeof initalState;

const reducer = createReducer(initalState, (builder) => {
  builder.addCase(scheduleActions.contactAdded, (state, action) => {
    state.contacts.push({
      id: state.contacts.length,
      name: action.payload.name,
      phone: action.payload.phone,
    });
  });
});
```

Si eres curioso. La manera en como builder encadena todos los casos sigue [fluent Style](https://en.wikipedia.org/wiki/Fluent_interface)😶 .


Hay algo notable aquí, y es que al parecer no estamos siguiendo el primer principio de Redux, que dice que el estado es de solo lectura, ose es inmutable. Bueno podemos ahorrarnos esa preocupación con Immer, el cual explicaré en el siguiente parte :).

Happy Coding😄