La mejor manera de diseñar su aplicación Redux

Este artículo trata sobre cómo pensar en Redux. Intentaremos entender cómo podemos utilizar esta maravillosa biblioteca para hacer que nuestra aplicación sea más estable, robusta y fácil de mantener. Es un idioma independiente, sin embargo, mantendremos nuestro alcance en Redux con React.

Para aquellos que no han usado Redux antes, citaré de los documentos:

Redux es un contenedor de estado predecible para aplicaciones JavaScript.

Es solo una biblioteca de 2kb que resuelve uno de los mayores problemas en el mantenimiento de grandes aplicaciones de JavaScript: la administración del estado.

Este artículo no trata sobre Redux, ya que hay muchos artículos al respecto. Más bien, se trata de cómo deberíamos visualizar una aplicación Redux y usarla de manera efectiva.

Digamos que estamos creando una aplicación de comercio electrónico en la que tiene algunas páginas básicas como catálogo, detalles del producto y éxito del pago.

A continuación se muestran los esquemas de cómo se vería la aplicación:

Entonces, la arquitectura en Redux significa que tenemos que visualizar la aplicación como una entidad, y cada página es una subentidad.

Hay cuatro etapas para crear una aplicación Redux:

  1. Visualiza el árbol de estado
  2. Diseña tus reductores
  3. Implementar acciones
  4. Implementar presentación

Paso 1: Visualice el árbol de estado

De los wireframes de arriba, diseñemos nuestro árbol de estado.

Éste es el paso más importante. Una vez que hayamos terminado de visualizar nuestro árbol de estado, ¡implementar las técnicas de Redux se vuelve realmente fácil! Los círculos punteados son estados que serán compartidos por la aplicación, los círculos sólidos son estados específicos de la página.

Paso 2: diseña tus reductores

En caso de que se esté preguntando qué es exactamente un reductor, citaré directamente de los documentos:

Los reductores especifican cómo cambia el estado de la aplicación en respuesta a las acciones enviadas a la tienda. Recuerde que las acciones solo describen lo que sucedió , pero no describen cómo cambia el estado de la aplicación.

Cada uno de los estados que son importantes puede tener sus propios reductores. Más tarde, podemos combinarlos en un reductor de raíz que finalmente definirá la tienda (la única fuente de verdad de la aplicación). Aquí es donde entra el poder real: tienes control total sobre tus estados y su comportamiento. Nada pasa desapercibido en la tienda. El observador silencioso vigila.

Veamos un ejemplo de cómo diseñar un reductor con la ayuda del árbol de estado de la aplicación que diseñamos anteriormente.

// Root Reducer const rootReducer = combineReducer({ header: headerReducer, login: loginReducer, footer: footerReducer, common: commonReducer, product: productReducer, catalog: catalogReducer, payment: paymentReducer });

El reductor de raíces lo dice todo. Contiene todo lo que la tienda necesita saber sobre la aplicación.

Ahora veamos cómo se ve una subentidad headerReducer.

¿Recuerda cómo diseñamos nuestro estado de encabezado?

// Header Reducer const headerReducer = combineReducer({ menu: menuReducer, search: searchReducer, location: locationReducer });

Nuestro reductor es una réplica de lo que diseñamos anteriormente en nuestro árbol de estado. Este es el poder de la visualización.

Observe cómo un reductor contiene más reductores. No necesitamos crear un reductor enorme. Se puede dividir fácilmente en reductores más pequeños, ya que cada uno tiene sus identidades individuales y sus propias operaciones específicas. Esto nos ayuda a crear una separación de la lógica, que es muy importante para mantener aplicaciones grandes.

Ahora entendamos cómo se debe configurar un archivo reductor típico, por ejemplo, searchReducer.

// Search Reducer const initialState = { payload: [], isLoading: false, error: {}}; export function searchReducer( state=initialState, action ) { switch(action.type) { case FETCH_SEARCH_DATA: return { ...state, isLoading: true }; case FETCH_SEARCH_SUCCESS: return { ...state, payload: action.payload, isLoading: false }; case FETCH_SEARCH_FAILURE: return { ...state, error: action.error, isLoading: false }; case RESET_SEARCH_DATA: return { ...state, ...initialState } default: return state; } }

Este patrón reductor define los cambios posibles en su estado de búsqueda cuando se llama a la API de búsqueda.

FETCH_SEARCH_DATA, FETCH_SEARCH_SUCCESS, FETCH_SEARCH_FAILURE, RESET_SEARCH_DATA

Todo lo anterior son posibles constantes que definen qué posibles acciones se pueden realizar.

Nota: Es importante mantener una acción RESET_SEARCH_DATA, en caso de que necesitemos restablecer los datos durante el desmontaje de un componente.

Paso 3: Implementar acciones

Cada acción que tiene llamadas a la API generalmente pasa por tres etapas en una aplicación.

  1. Estado de carga -> FETCH_SEARCH_DATA
  2. Éxito -> FETCH_SEARCH_SUCCESS
  3. Fallo -> FETCH_SEARCH_FAILURE

Mantener estos tipos de acciones nos ayuda a verificar el flujo de datos cuando se llama a una API en nuestra aplicación.

Profundicemos en el código para comprender cómo se verá una acción típica.

export function fetchSearchData(args) { return async (dispatch) => { // Initiate loading state dispatch({ type: FETCH_SEARCH_DATA }); try { // Call the API const result = await fetchSearchData( args.pageCount, args.itemsPerPage ); // Update payload in reducer on success dispatch({ type: FETCH_SEARCH_SUCCESS, payload: result, currentPage: args.pageCount }); } catch (err) { // Update error in reducer on failure dispatch({ type: FETCH_SEARCH_FAILURE, error: err }); } }; }

Observe cómo la tienda realiza un seguimiento del flujo de datos mediante acciones. Esto hace responsable a todos y cada uno de los cambios en la aplicación.

Por tanto, se escriben acciones similares para cada cambio en los reductores de varios estados.

Uno de los mayores beneficios de Redux es la abstracción de todas y cada una de las acciones.

Paso 4: Implementar la presentación

import React, { Component } from 'react'; import { connect } from 'react-redux';; import fetchSearchData from './action/fetchSearchData'; import SearchData from './SearchData'; const Search = (props) => (  ); const mapStateToProps = (state) => ({ search: state.header.search.payload }); const mapDispatchToProps = { fetchSearchData}; export default connect(mapStateToProps, mapDispatchToProps)(Search)

As you can see, the presentation component is very simple and easy to understand.

Conclusion

I would like to mention some of the biggest benefits that I found using Redux:

  1. It certainly reduces code smell.
  2. Abstraction of code is easier to achieve.
  3. Redux also introduces us to other principles like immutability, functional programming, and many others.
  4. It allows you to visualise each and every action and track them with “time traveling.”

I hope this article helps you get a clearer picture of why Redux is truly awesome, and how we can utilise the power of visualisation to make maintainable applications.

Follow me on twitter to get more updates regarding new articles and to stay updated in latest frontend developments. Also share this article on twitter to help others know about it. Sharing is caring ^_^.

Some helpful resources

  1. //redux.js.org/
  2. //github.com/reduxjs/redux/blob/master/examples
  3. //medium.com/@rajaraodv/a-guide-for-building-a-react-redux-crud-app-7fe0b8943d0f#.c4yhhvk0d

My previous articles

  1. //medium.freecodecamp.org/how-to-use-redux-persist-when-migrating-your-states-a5dee16b5ead
  2. //codeburst.io/redux-observable-to-the-rescue-b27f07406cf2
  3. //codeburst.io/building-webapp-for-the-future-68d69054cbbd
  4. //codeburst.io/cors-story-of-requesting-twice-85219da7172d