Desde que aparecieron los ganchos en React, ha habido muchas preguntas sobre si pueden reemplazar a Redux.
Creo que los ganchos y Redux tienen poco en común. Los ganchos no nos dan nuevas oportunidades increíbles para trabajar con el estado. En cambio, extienden las API para que puedan hacer en React lo que ya era posible en él. Sin embargo, la API de enlace ha hecho que trabajar con las capacidades de administración de estado de React sea mucho más conveniente. Resultó que es más fácil usar las nuevas capacidades para trabajar con el estado que las antiguas que estaban disponibles en los componentes según las clases. Ahora uso las herramientas para trabajar con el estado de los componentes con mucha más frecuencia que antes. Naturalmente, hago esto solo cuando es apropiado.

Para explicar mi actitud hacia React Hooks y Redux, primero me gustaría hablar sobre las situaciones en las que se usa Redux.
¿Qué es redux?
Redux es una biblioteca que implementa almacenamiento de estado de aplicación predecible. También es una arquitectura que se integra perfectamente con React.
Estas son las principales fortalezas de Redux:
- Representación determinista del estado (en combinación con componentes puros, esto hace posible formar elementos visuales deterministas).
- Soporte para cambios de estado transaccional.
- Aislamiento de la gestión estatal de los mecanismos de E / S y los efectos secundarios.
- La presencia de una sola fuente de datos confiables para el estado.
- Fácil organización de la colaboración con el estado en varios componentes.
- Herramientas de análisis transaccional (registro automático de objetos de acción).
- Depuración con la capacidad de grabar y reproducir el proceso de ejecución del programa (Time Travel Debugging, TTD).
En otras palabras, Redux le permite organizar bien su código y le permite depurarlo convenientemente. Redux ayuda a desarrollar aplicaciones que son fáciles de mantener. El uso de esta biblioteca hace que sea más fácil encontrar las fuentes de problemas que surgen en los programas.
¿Qué son los ganchos React?
Los ganchos de reacción permiten, cuando se trabaja con componentes funcionales, usar un análogo del estado de los componentes basado en clases y análogos de sus métodos de ciclo de vida. Los ganchos aparecieron en React 16.8.
Entre las principales fortalezas de los ganchos se encuentran las siguientes:
- La capacidad de usar el estado y manejar los eventos del ciclo de vida de los componentes sin usar componentes basados en clases.
- Almacenamiento conjunto de la lógica relacionada en la misma ubicación del componente en lugar de dividir una lógica similar entre varios métodos de ciclo de vida.
- Compartir mecanismos independientes de la implementación del componente (esto es similar a la plantilla de renderizado ).
Tenga en cuenta que estas excelentes funciones no abruman a Redux. Los ganchos de reacción pueden y deben usarse para realizar actualizaciones de estado deterministas, pero esta siempre ha sido una de las características de Reacción, y el modelo de estado determinista de Redux combina bien con esta característica. Así es como React logra el determinismo en la salida de elementos visuales, y este es, sin exagerar, uno de los motivos principales para crear React.
Si usa herramientas como
la API react-redux con ganchos , o el
gancho React useReducer , encontrará que no hay razón para preguntar qué elegir: ganchos o Redux. Puede usar ambas, combinando y combinando estas tecnologías.
¿Qué reemplazan los ganchos?
Después del advenimiento de las API de gancho, dejé de usar las siguientes tecnologías:
¿Qué no reemplaza los ganchos?
Todavía uso a menudo las siguientes tecnologías:
- Redux: por todas las razones anteriores.
- Componentes de orden superior: con el fin de realizar la composición de componentes en los casos en que tengo que implementar una funcionalidad de extremo a extremo compartida por todos o algunos de los componentes visuales de la aplicación. Dicha funcionalidad incluye proveedores de Redux, sistemas de diseño de página, sistemas de soporte de configuración de aplicaciones, herramientas de autenticación y autorización, herramientas de internacionalización de aplicaciones, etc.
- Separación entre componentes del contenedor y componentes que tienen una representación visual. Esto le permite mejorar la modularidad y la capacidad de prueba de las aplicaciones, es mejor separar los efectos y la lógica pura.
¿Cuándo usar ganchos?
No es necesario esforzarse por usar Redux en cada aplicación y en cada componente. Si su proyecto consta de un componente visual, si no guarda datos y no carga datos desde allí, si no se realizan operaciones de E / S asíncronas, entonces no puedo pensar en una razón valiosa para complicar este proyecto al usar Redux en él.
Lo mismo puede decirse de los componentes que tienen las siguientes características:
- No usan recursos de red.
- No almacenan datos en estado y no los cargan desde allí.
- No comparten el estado con otros componentes que no son sus descendientes.
- No tienen un cierto estado propio, utilizado para el almacenamiento de datos a corto plazo.
Puede tener una buena razón para usar el modelo de estado del componente React estándar en ciertas situaciones. En situaciones como esta, los ganchos React te harán un buen trabajo. Por ejemplo, el formulario que se describe a continuación utiliza el estado local del componente utilizando el
useState
React
useState
.
import React, { useState } from 'react'; import t from 'prop-types'; import TextField, { Input } from '@material/react-text-field'; const noop = () => {}; const Holder = ({ itemPrice = 175, name = '', email = '', id = '', removeHolder = noop, showRemoveButton = false, }) => { const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email); const setter = set => e => { const { target } = e; const { value } = target; set(value); }; return ( <div className="row"> <div className="holder"> <div className="holder-name"> <TextField label="Name"> <Input value={nameInput} onChange={setter(setName)} required /> </TextField> </div> <div className="holder-email"> <TextField label="Email"> <Input value={emailInput} onChange={setter(setEmail)} type="email" required /> </TextField> </div> {showRemoveButton && ( <button className="remove-holder" aria-label="Remove membership" onClick={e => { e.preventDefault(); removeHolder(id); }} > × </button> )} </div> <div className="line-item-price">${itemPrice}</div> <style jsx>{cssHere}</style> </div> ); }; Holder.propTypes = { name: t.string, email: t.string, itemPrice: t.number, id: t.string, removeHolder: t.func, showRemoveButton: t.bool, }; export default Holder;
Aquí
useState
usa para controlar el estado brevemente usado de los campos de entrada de
name
y
email
:
const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email);
Puede notar que todavía hay un creador de la acción
removeHolder
que entra en las propiedades de Redux. Como ya se mencionó, la combinación y combinación de tecnologías es completamente normal.
Usar el estado local de un componente para resolver tales problemas siempre se ha visto bien, pero antes de React Hooks, en cualquier caso, hubiera querido guardar los datos del componente en el almacenamiento de Redux y obtener el estado de las propiedades.
Anteriormente, trabajar con el estado de un componente implicaba el uso de componentes basados en clases, escribir los datos iniciales en el estado utilizando los mecanismos para declarar las propiedades de la clase (o en el constructor de la clase), y así sucesivamente. Como resultado, resultó que para evitar usar Redux, el componente tenía que ser demasiado complicado. Redux también habló a favor de la existencia de herramientas convenientes para administrar el estado de los formularios con Redux. Como resultado, antes, no me habría preocupado que el estado temporal del formulario se almacene en el mismo lugar que los datos con una vida útil más larga.
Como ya he usado Redux en todas mis aplicaciones más o menos complejas, la elección de la tecnología para almacenar el estado de los componentes de los componentes no me hizo pensar mucho. Acabo de usar Redux en casi todos los casos.
En las condiciones modernas, hacer una elección también es fácil: trabajar con el estado del componente se organiza utilizando mecanismos estándar React y administrar el estado de la aplicación usando Redux.
¿Cuándo usar Redux?
Otra pregunta común con respecto a la administración del estado es: “¿Necesito poner absolutamente todo en el repositorio de Redux? Si no lo hago, ¿violará la capacidad de depurar aplicaciones usando mecanismos TTD? "
No es necesario alojar absolutamente todo en el repositorio de Redux. El hecho es que las aplicaciones usan una gran cantidad de datos temporales que están demasiado dispersos a su alrededor para proporcionar información que, al registrarse en el registro o usarse durante la depuración, puede proporcionar al desarrollador una ayuda notable para encontrar problemas. Probablemente usted, a menos que esté escribiendo una aplicación de editor en tiempo real, no necesita escribir en el estado cada movimiento del mouse o cada pulsación de tecla. Cuando coloca algo en un estado Redux, agrega un nivel adicional de abstracción a la aplicación, así como un nivel adicional de complejidad que lo acompaña.
En otras palabras, puede usar Redux de manera segura, pero debe haber alguna razón para esto. El uso de las funciones de Redux en los componentes puede estar justificado si los componentes difieren en las siguientes funciones:
- Usan E / S. Por ejemplo, trabajan con una red o con ciertos dispositivos.
- Guardan datos o cargan datos de ellos.
- Trabajan con su estado junto con componentes que no son sus descendientes.
- Se ocupan de cualquier lógica de negocios con la que se ocupan otras partes de la aplicación: procesan datos que se utilizan en otras partes de la aplicación.
Aquí hay otro ejemplo tomado de la aplicación
TDDDay :
import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { compose } from 'ramda'; import page from '../../hocs/page.js'; import Purchase from './purchase-component.js'; import { addHolder, removeHolder, getHolders } from './purchase-reducer.js'; const PurchasePage = () => {
Este documento no trata el DOM. Este es un componente de presentación. Está conectado a Redux usando
la API react-redux con soporte de gancho .
Aquí se usa Redux porque necesitamos que los datos que maneja este formulario se usen en otras partes de la interfaz de usuario. Y después de completar la operación de compra, necesitamos guardar la información relevante en la base de datos.
Los fragmentos de estado con los que funciona este código son utilizados por varios componentes; no son procesados por un solo componente. Esto no es información, existe solo un corto período de tiempo. Estos datos pueden considerarse permanentes, pueden usarse en diferentes pantallas de la aplicación y en varias sesiones. Todos estos son escenarios en los que no se pueden aplicar los estados del componente para almacenar datos. Es cierto que esto todavía es posible, pero solo si el creador de la aplicación escribe, basándose en la API React, su propia biblioteca para administrar el estado. Esto es mucho más difícil de hacer que simplemente usar Redux.
La API React
Suspense , en el futuro, puede ser útil al almacenar datos en un estado y cargarlos desde allí. Necesitamos esperar su lanzamiento y ver si puede reemplazar las plantillas para guardar y cargar datos de Redux. Redux nos permite separar claramente los efectos secundarios del resto de la lógica del componente, mientras que no necesitamos trabajar con servicios de E / S de una manera especial. (La razón por la que prefiero la biblioteca
redux-saga al
middleware redux-thunk es el aislamiento del efecto). Para competir con Redux en este escenario, la API React necesitará proporcionar aislamiento del efecto.
Redux es arquitectura
Redux es mucho más (y a menudo mucho menos) que una biblioteca de administración de estado. También es un subconjunto de la arquitectura
Flux , que define mucho más estrictamente cómo se implementan los cambios de estado. Lea más sobre la arquitectura Redux
aquí .
A menudo uso reductores creados en el estilo Redux en aquellos casos en que necesito mantener el estado complejo del componente, pero no necesito usar la biblioteca Redux. También utilizo acciones creadas en el espíritu de Redux (e incluso herramientas de Redux como
Autodux y
redux-saga ) para enviar acciones a las aplicaciones Node.js. Sin embargo, ni siquiera importo Redux en tales aplicaciones.
El proyecto Redux siempre ha sido más una arquitectura y un conjunto de acuerdos voluntarios que una biblioteca. De hecho, la implementación básica de Redux se puede establecer literalmente en un par de docenas de líneas de código.
Esto resultará ser una buena noticia para aquellos que desean usar el estado local de componentes con ganchos con más frecuencia y no vincular todo a Redux.
React admite el gancho
useReducer
, que puede funcionar con reductores de estilo Redux. Esto es bueno para implementar una lógica no trivial de trabajar con estado, para trabajar con fragmentos de estado dependientes, etc. Si encuentra un problema para el cual el estado temporal de un componente individual es adecuado, puede usar la arquitectura Redux para trabajar con este estado, pero en lugar de la biblioteca Redux, puede usar el
useReducer
para administrar el estado.
Si más tarde necesita establecer un almacenamiento permanente de datos que anteriormente solo almacenaba temporalmente, entonces estará 90% listo para tal cambio. Todo lo que tiene que hacer es conectar el componente al repositorio de Redux y agregar allí el reductor correspondiente.
Preguntas y respuestas
¿Se rompe el determinismo si Redux no gestiona todos los datos de la aplicación?
No, no está roto. De hecho, el uso de Redux no hace que un proyecto sea determinista. Pero los acuerdos son. Si desea que su estado de Redux sea determinista, use
funciones puras . Lo mismo se aplica a situaciones en las que es necesario que se determine el estado temporal de los componentes locales.
▍ ¿Debería la biblioteca Redux desempeñar el papel de una sola fuente de datos confiables?
El principio de una sola fuente de datos confiables no indica que sea necesario que todos los datos incluidos en el estado de la aplicación se almacenen en un solo lugar. El significado de este principio es que cada fragmento del estado debe tener solo una fuente de datos confiables. Como resultado, podemos tener muchos fragmentos de estado, cada uno de los cuales tiene su propia fuente de datos confiables.
Esto significa que el programador puede decidir qué se transfiere a Redux y qué se transfiere al estado de los componentes. Los datos que determinan el estado también se pueden tomar de otras fuentes. Por ejemplo, desde una API de navegador que le permite trabajar con información sobre la dirección de la página que está viendo.
Redux es una gran herramienta para soportar una sola fuente de datos confiables para el estado de una aplicación. Pero si el estado del componente se encuentra y se usa exclusivamente dentro de este componente, entonces, por definición, este estado ya tiene una única fuente de datos confiables: el estado del componente Reaccionar.
Si coloca algunos datos en el estado Redux, siempre debe leer estos datos del estado Redux. Para todo lo que está en el repositorio de Redux, este repositorio debería ser la única fuente de datos confiables.
Poner todo en un estado Redux, si es necesario, es perfectamente normal. Quizás esto afectará el rendimiento si usa fragmentos de estado que necesitan actualizarse con frecuencia, o si está hablando de almacenar el estado de un componente en el que los fragmentos de estado dependiente se usan mucho. No debe preocuparse por el rendimiento hasta que haya problemas con el rendimiento. Pero si le preocupa el problema del rendimiento, pruebe ambas formas de trabajar con el estado y evalúe su impacto en el rendimiento. Perfile su proyecto y recuerde el modelo de rendimiento RAIL.
▍ ¿Necesito usar la función de conexión de react-redux, o es mejor usar ganchos?
Depende mucho. La función de
connect
crea un componente de orden superior adecuado para uso repetido, y los ganchos están optimizados para la integración con un solo componente.
¿Necesito conectar las mismas propiedades a diferentes componentes? Si es así, usa
connect
. De lo contrario, preferiría elegir ganchos. Por ejemplo, imagine que tiene un componente responsable de autorizar permisos para las acciones del usuario:
import { connect } from 'react-redux'; import RequiresPermission from './requires-permission-component'; import { userHasPermission } from '../../features/user-profile/user-profile-reducer'; import curry from 'lodash/fp/curry'; const requiresPermission = curry( (NotPermittedComponent, { permission }, PermittedComponent) => { const mapStateToProps = state => ({ NotPermittedComponent, PermittedComponent, isPermitted: userHasPermission(state, permission), }); return connect(mapStateToProps)(RequiresPermission); }, ); export default requiresPermission;
Ahora, si un administrador está trabajando intensamente con la aplicación, cuyas acciones necesitan un permiso especial, puede crear un componente de orden superior que combine todos estos permisos con todas las funcionalidades necesarias de extremo a extremo:
import NextError from 'next/error'; import compose from 'lodash/fp/compose'; import React from 'react'; import requiresPermission from '../requires-permission'; import withFeatures from '../with-features'; import withAuth from '../with-auth'; import withEnv from '../with-env'; import withLoader from '../with-loader'; import withLayout from '../with-layout'; export default compose( withEnv, withAuth, withLoader, withLayout(), withFeatures, requiresPermission(() => <NextError statusCode={404} />, { permission: 'admin', }), );
Aquí se explica cómo usarlo:
import compose from 'lodash/fp/compose'; import adminPage from '../HOCs/admin-page'; import AdminIndex from '../features/admin-index/admin-index-component.js'; export default adminPage(AdminIndex);
La API de componente de orden superior es conveniente para esta tarea. Le permite resolverlo de manera más sucinta, usando menos código que usando ganchos. Pero para usar la función de
connect
, debe recordar que toma
mapStateToProps
como primer argumento y
mapStateToProps
como segundo. No debemos olvidar que esta función puede tomar funciones o literales de objeto. Necesita saber cómo difieren los diferentes usos de
connect
, y que esta es una función con curry, pero su curry no se realiza automáticamente.
En otras palabras, puedo decir que creo que durante el desarrollo de la
connect
, se realizó mucho trabajo en la dirección de la concisión del código, pero el código resultante no es particularmente legible ni particularmente conveniente. Si no necesito trabajar con varios componentes, con gusto preferiré la API de
connect
inconveniente a una API de enlace mucho más conveniente, incluso teniendo en cuenta que esto conducirá a un aumento en la cantidad de código.
▍ Si un singleton se considera un antipatrón y Redux es un singleton, ¿significa esto que Redux es un antipatrón?
No, no lo hace. El uso de un singleton en el código insinúa la dudosa calidad de este código, lo que indica la presencia de un estado mutable compartido en él. Este es un verdadero antipatrón. Redux, por otro lado, evita la mutación del estado compartido a través de la encapsulación (no debe cambiar el estado de la aplicación directamente, fuera de los reductores; Redux resuelve el problema de cambiar el estado) y enviando mensajes (solo el objeto de evento enviado puede causar un cambio de estado).
Resumen
¿Redux reemplaza los ganchos React? Los ganchos son geniales, pero no reemplazan a Redux.
Esperamos que este material lo ayude a elegir un modelo de gestión estatal para sus proyectos React.
Estimados lectores! ¿Has encontrado situaciones en las que los ganchos React pueden reemplazar a Redux?
