11 consejos para usar Redux al desarrollar aplicaciones React

Cuando se trata de desarrollar aplicaciones React, en términos de arquitectura de código, los proyectos pequeños suelen ser más flexibles que los grandes. No hay nada de malo en crear tales proyectos utilizando pautas prácticas destinadas a aplicaciones más grandes. Pero todo esto, en el caso de pequeños proyectos, puede ser simplemente innecesario. Cuanto más pequeña es la aplicación, más "condescendiente" se refiere al uso de soluciones simples, posiblemente no óptimas, pero que no requieren mucho tiempo para su implementación.



A pesar de esto, me gustaría señalar que algunas de las recomendaciones que se darán en este material están dirigidas a React aplicaciones de cualquier escala.

Si nunca ha creado una aplicación de producción, este artículo puede ayudarlo a prepararse para el desarrollo de soluciones a gran escala. Algo así podría convertirse en uno de tus próximos proyectos. Lo peor que le puede pasar a un programador es cuando trabaja en un proyecto y se da cuenta de que necesita refactorizar grandes cantidades de código para mejorar la escalabilidad y la capacidad de mantenimiento de la aplicación. Todo se ve aún peor si no hubo pruebas unitarias en el proyecto antes de la refactorización.

El autor de este material le pide al lector que confíe en él. Ha estado en situaciones similares. Entonces, consiguió varias tareas que debían resolverse en un tiempo determinado. Al principio, pensó que todo lo que hizo fue excelente. La fuente de tales pensamientos fue que su aplicación web, después de hacer cambios, continuó funcionando, y al mismo tiempo continuó funcionando rápidamente. Sabía cómo usar Redux, cómo establecer una interacción normal entre los componentes de la interfaz de usuario. Le pareció que entendía profundamente los conceptos de reductores y acciones. Se sintió invulnerable.

Pero aquí el futuro se arrastró.

Después de un par de meses de trabajo en la aplicación, se le agregaron más de 15 nuevas características. Después de eso, el proyecto se salió de control. El código que usó la biblioteca Redux se ha vuelto muy difícil de mantener. ¿Por qué sucedió esto? Al principio, ¿no parecía que el proyecto esperaba una vida larga y sin nubes?

El autor del artículo dice que, al hacer preguntas similares, se dio cuenta de que había plantado una bomba de tiempo en el proyecto con sus propias manos.

La biblioteca Redux, si se usa correctamente en proyectos grandes, ayuda, a medida que estos proyectos crecen, a mantener su código en un estado compatible.

Aquí hay 11 consejos para aquellos que desean desarrollar aplicaciones React escalables usando Redux.

1. No coloque el código de acción y las constantes en un solo lugar


Puede encontrar algunos tutoriales de Redux en los que las constantes y todas las acciones se colocan en el mismo lugar. Sin embargo, este enfoque, a medida que crece la aplicación, puede generar problemas rápidamente. Las constantes deben almacenarse por separado, por ejemplo, en ./src/constants . Como resultado, para buscar constantes, debe mirar solo una carpeta, y no varias.

Además, la creación de archivos separados que almacenan acciones parece completamente normal. Dichos archivos encapsulan acciones directamente relacionadas entre sí. Las acciones en un solo archivo, por ejemplo, pueden tener similitudes en términos de qué y cómo se usan.

Supongamos que está desarrollando un arcade o un juego de rol y está creando las clases warrior (guerrero), sorceress (hechicera) y archer (arquero). En tal situación, puede lograr un alto nivel de soporte de código organizando las acciones de la siguiente manera:

 src/actions/warrior.js src/actions/sorceress.js src/actions/archer.js 

Será mucho peor si todo cae en un archivo:

 src/actions/classes.js 

Si la aplicación se vuelve muy grande, podría ser incluso mejor usar aproximadamente la siguiente estructura de división de código en archivos:

 src/actions/warrior/skills.js src/actions/sorceress/skills.js src/actions/archer/skills.js 

Aquí solo se muestra un pequeño fragmento de dicha estructura. Si cree que utiliza este enfoque de manera más amplia y consistente, terminará con algo como este conjunto de archivos:

 src/actions/warrior/skills.js src/actions/warrior/quests.js src/actions/warrior/equipping.js src/actions/sorceress/skills.js src/actions/sorceress/quests.js src/actions/sorceress/equipping.js src/actions/archer/skills.js src/actions/archer/quests.js src/actions/archer/equipping.js 

Así es como se vería la acción del archivo src/actions/sorceress/skills para el objeto sorceress :

 import { CAST_FIRE_TORNADO, CAST_LIGHTNING_BOLT } from '../constants/sorceress' export const castFireTornado = (target) => ({ type: CAST_FIRE_TORNADO, target, }) export const castLightningBolt = (target) => ({ type: CAST_LIGHTNING_BOLT, target, }) 

Aquí está el contenido del src/actions/sorceress/equipping :

 import * as consts from '../constants/sorceress' export const equipStaff = (staff, enhancements) => {...} export const removeStaff = (staff) => {...} export const upgradeStaff = (slot, enhancements) => { return (dispatch, getState, { api }) => {   //                 const state = getState()   const currentEquipment = state.classes.sorceress.equipment.current   const staff = currentEquipment[slot]   const isMax = staff.level >= 9   if (isMax) {     return   }   dispatch({ type: consts.UPGRADING_STAFF, slot })   api.upgradeEquipment({     type: 'staff',     id: currentEquipment.id,     enhancements,   })   .then((newStaff) => {     dispatch({ type: consts.UPGRADED_STAFF, slot, staff: newStaff })   })   .catch((error) => {     dispatch({ type: consts.UPGRADE_STAFF_FAILED, error })   }) } } 

La razón por la que organizamos el código de esta manera es porque constantemente se agregan nuevas características a los proyectos. Esto significa que debemos estar preparados para su aparición y, al mismo tiempo, esforzarnos por garantizar que los archivos no estén sobrecargados con código.

Al comienzo del trabajo en el proyecto, esto puede parecer innecesario. Pero cuanto más grande sea el proyecto, más fuerte se sentirá la fuerza de tal enfoque.

2. No coloque el código reductor en un lugar


Cuando veo que el código de mis reductores se convierte en algo similar al que se muestra a continuación, entiendo que necesito cambiar algo.

 const equipmentReducers = (state, action) => { switch (action.type) {   case consts.UPGRADING_STAFF:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: action.slot,           },         },       },     }   case consts.UPGRADED_STAFF:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: null,             current: {               ...state.classes.sorceress.equipment.current,               [action.slot]: action.staff,             },           },         },       },     }   case consts.UPGRADE_STAFF_FAILED:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: null,           },         },       },     }   default:     return state } } 

Tal código, sin duda, podría conducir rápidamente a un gran desorden. Por lo tanto, es mejor mantener la estructura del trabajo con el estado de la forma más simple posible, apuntando al nivel mínimo de su anidamiento. En su lugar, puede intentar recurrir a la composición de los reductores.

Un truco útil para trabajar con reductores es crear un reductor de orden superior que generen otros reductores. Lea más sobre esto aquí.

3. Use nombres informativos de variables


Nombrar variables, a primera vista, puede parecer una tarea elemental. Pero, de hecho, esta tarea puede ser una de las más difíciles.

La selección de nombres de variables es generalmente relevante para pautas prácticas para escribir código limpio. La razón por la cual existe un "nombre de variable" en general es porque este aspecto del desarrollo del código juega un papel muy importante en la práctica. La selección incorrecta de nombres de variables es una forma segura de hacerse daño a usted mismo y a los miembros de su equipo en el futuro.

¿Alguna vez trató de editar el código de otra persona y al mismo tiempo encontró dificultades para comprender qué hace exactamente este código? ¿Alguna vez ha ejecutado un programa extranjero y descubrió que no funciona como se esperaba?

Yo diría que para demostrar que en tales casos ha encontrado el llamado "código sucio".

Si tiene que lidiar con un código similar en aplicaciones grandes, entonces esto es solo una pesadilla. Desafortunadamente, esto sucede con bastante frecuencia.

Aquí hay un caso de la vida. Edité el código de enlace React de una aplicación y en ese momento me enviaron una tarea. Fue para implementar en la aplicación la capacidad de mostrar información adicional sobre los médicos. Esta información debería haberse mostrado al paciente que hace clic en la foto de perfil del médico. Era necesario sacarlo de la tabla, tenía que llegar al cliente después de procesar la siguiente solicitud al servidor.

Esta no fue una tarea difícil, el principal problema que encontré fue que tuve que pasar demasiado tiempo para encontrar dónde estaba exactamente lo que necesitaba en el código del proyecto.

Busqué en el código las palabras info , dataToSend , dataObject y otras que, en mi dataObject , están asociadas con los datos recibidos del servidor. Después de 5-10 minutos, logré encontrar el código responsable de trabajar con los datos que necesitaba. El objeto en el que terminaron se llamaba paymentObject . En mi opinión, un objeto relacionado con los pagos puede contener algo como un código CVV, número de tarjeta de crédito, código postal del pagador y otra información similar. El objeto que descubrí tenía 11 propiedades. Solo tres de ellos estaban relacionados con pagos: método de pago, identificador de perfil de pago y una lista de códigos de cupones.

La situación tampoco mejoró porque tuve que hacer cambios en este objeto que fueron necesarios para resolver la tarea que tenía ante mí.

En resumen, se recomienda abstenerse de usar nombres oscuros para funciones y variables. Aquí hay un código de ejemplo en el que el nombre de la función de notify no revela su significado:

 import React from 'react' class App extends React.Component { state = { data: null } //  -? notify = () => {   if (this.props.user.loaded) {     if (this.props.user.profileIsReady) {       toast.alert(         'You are not approved. Please come back in 15 minutes or you will be deleted.',         {           position: 'bottom-right',           timeout: 15000,         },       )     }   } } render() {   return this.props.render({     ...this.state,     notify: this.notify,   }) } } export default App 

4. No cambie estructuras o tipos de datos en flujos de datos de aplicaciones ya configurados


Uno de los errores más grandes que he cometido fue cambiar la estructura de datos en un flujo de datos de aplicaciones ya configurado. La nueva estructura de datos traería un gran aumento de rendimiento, ya que utilizaba métodos rápidos para buscar datos en objetos almacenados en la memoria, en lugar de iterar sobre matrices. Pero ya era demasiado tarde.

Te pido que no hagas esto. Quizás algo como esto solo se puede ofrecer a alguien que sepa exactamente qué partes de la aplicación puede afectar.

¿Cuáles son las consecuencias de tal paso? Por ejemplo, si algo fue primero una matriz y luego se convirtió en un objeto, esto puede interrumpir el funcionamiento de muchas partes de la aplicación. Cometí un gran error al creer que podía recordar todos los lugares en el código que podrían verse afectados por un cambio en la presentación de datos estructurados. Sin embargo, en tales casos, siempre hay algún fragmento de código que se ve afectado por el cambio y que nadie recuerda.

5. Usa fragmentos


Solía ​​ser fanático del editor Atom, pero cambié a VS Code debido a que este editor era increíblemente rápido en comparación con Atom. Y él, a su velocidad, admite una gran cantidad de posibilidades diferentes.

Si también usa VS Code, le recomiendo instalar la extensión Project Snippets . Esta extensión permite al programador crear fragmentos personalizados para cada espacio de trabajo utilizado en un proyecto. Esta extensión funciona de la misma manera que el mecanismo Use Snippets integrado en VS Code. La diferencia es que cuando se trabaja con fragmentos de proyecto, se crea la carpeta .vscode/snippets/ en el proyecto. Se parece a la siguiente figura.


El contenido de la carpeta .vscode / snippets /

6. Crear pruebas unitarias, integrales y de integración


A medida que crece el tamaño de la aplicación, se vuelve más aterrador para el programador editar código que no está cubierto por las pruebas. Por ejemplo, puede suceder que alguien haya editado el código almacenado en src/x/y/z/ y haya decidido enviarlo a producción. Si al mismo tiempo los cambios realizados afectan aquellas partes del proyecto en las que el programador no pensó, entonces todo puede terminar en un error que un usuario real encontrará. Si hay pruebas en el proyecto, el programador sabrá sobre el error mucho antes de que el código entre en producción.

7. Lluvia de ideas


Los programadores, en el proceso de introducir nuevas características en los proyectos, a menudo se niegan a hacer una lluvia de ideas. Esto sucede porque dicha actividad no está relacionada con la escritura de código. Esto sucede especialmente a menudo cuando se asigna muy poco tiempo para la tarea.

¿Y por qué, por cierto, tienes que hacer una lluvia de ideas durante el desarrollo de aplicaciones?

El hecho es que cuanto más compleja se vuelve la aplicación, más atención deben prestar los programadores a sus partes individuales. La lluvia de ideas ayuda a reducir el tiempo que lleva refactorizar el código. Después de su detención, el programador está armado con el conocimiento de lo que puede salir mal durante la finalización del proyecto. A menudo, los programadores, mientras desarrollan una aplicación, ni siquiera se molestan en pensar al menos un poco sobre cómo hacer todo de la mejor manera.

Es por eso que la lluvia de ideas es muy importante. Durante dicho evento, el programador puede considerar la arquitectura del código, pensar en cómo hacer los cambios necesarios en el programa, rastrear el ciclo de vida de estos cambios y crear una estrategia para trabajar con ellos. No vale la pena comenzar el hábito de mantener todos los planes exclusivamente en su cabeza. Esto es lo que hacen los programadores que tienen demasiada confianza. Pero recordar absolutamente todo es simplemente imposible. Y, tan pronto como se haga algo mal, aparecerán problemas uno tras otro. Este es el principio del dominó en acción.

La lluvia de ideas también es útil en equipos. Por ejemplo, si en el transcurso del trabajo alguien se encuentra con un problema, puede recurrir a los materiales de la sesión de lluvia de ideas, ya que el problema que surgió con él ya podría haberse considerado. Las notas que se hacen durante la sesión de lluvia de ideas pueden jugar el papel de un plan para resolver el problema. Este plan le permite evaluar claramente la cantidad de trabajo realizado.

8. Crear maquetas de aplicaciones


Si va a comenzar a desarrollar la aplicación, debe tomar una decisión sobre cómo se verá y cómo los usuarios interactuarán con ella. Esto significa que deberá crear un diseño de aplicación. Puede usar varias herramientas para esto.

Moqups es una de las herramientas de maquetas de aplicaciones que escucho a menudo. Esta es una herramienta rápida creada usando HTML5 y JavaScript y no impone requisitos especiales en el sistema.

Crear una aplicación simulada simplifica enormemente y acelera el proceso de desarrollo. El diseño le brinda al desarrollador información sobre la relación entre las partes individuales de la aplicación y sobre qué tipo de datos se mostrarán en sus páginas.

9. Planifique el flujo de datos en las aplicaciones


Casi todos los componentes de su aplicación estarán asociados con algunos datos. Algunos componentes usarán sus propias fuentes de datos, pero la mayoría de los componentes reciben datos de entidades por encima de ellos en la jerarquía de componentes. Para aquellas partes de la aplicación en las que varios componentes comparten los mismos datos, es útil proporcionar un almacenamiento de información centralizado ubicado en el nivel superior de la jerarquía. Es en tales situaciones que la biblioteca Redux puede proporcionar asistencia invaluable al desarrollador.

Recomiendo que mientras trabaja en la aplicación, elabore un diagrama que muestre las formas en que se mueven los datos en esta aplicación. Esto ayudará a crear un modelo de aplicación claro, además, estamos hablando del código y la percepción de la aplicación por parte del programador. Tal modelo ayudará, además, en la creación de reductores.

10. Use las funciones de acceso a datos


A medida que crece el tamaño de la aplicación, también lo hace el número de sus componentes. Y cuando aumenta el número de componentes, sucede lo mismo con la frecuencia de usar selectores (react-redux ^ v7.1) o mapStateToProps . Suponga que sus componentes o enlaces a menudo acceden a fragmentos de estado en diferentes partes de la aplicación utilizando una construcción como useSelector((state) => state.app.user.profile.demographics.languages.main) . Si es así, eso significa que debe pensar en crear funciones de acceso a datos. Los archivos con tales funciones deben almacenarse en un lugar público desde el cual los componentes y ganchos puedan importarlos. Las funciones similares pueden ser filtros, analizadores o cualquier otra función para la transformación de datos.

Aquí hay algunos ejemplos.

Por ejemplo, src/accessors puede contener el siguiente código:

 export const getMainLanguages = (state) => state.app.user.profile.demographics.languages.main 

Aquí está la versión que usa connect , que puede ubicarse a lo largo de la src/components/ViewUserLanguages :

 import React from 'react' import { connect } from 'react-redux' import { getMainLanguages } from '../accessors' const ViewUserLanguages = ({ mainLanguages }) => ( <div>   <h1>Good Morning.</h1>   <small>Here are your main languages:</small>   <hr />   {mainLanguages.map((lang) => (     <div>{lang}</div>   ))} </div> ) export default connect((state) => ({ mainLanguages: getMainLanguages(state), }))(ViewUserLanguages) 

Aquí está la versión que usa useSelector ubicado en src/components/ViewUserLanguages :

 import React from 'react' import { useSelector } from 'react-redux' import { getMainLanguages } from '../accessors' const ViewUserLanguages = ({ mainLanguages }) => { const mainLanguages = useSelector(getMainLanguages) return (   <div>     <h1>Good Morning.</h1>     <small>Here are your main languages:</small>     <hr />     {mainLanguages.map((lang) => (       <div>{lang}</div>     ))}   </div> ) } export default ViewUserLanguages 

Además, esfuércese por garantizar que dichas funciones sean inmutables, sin efectos secundarios. Descubre por qué doy una recomendación aquí .

11. Controle el flujo de datos en las propiedades utilizando la sintaxis de desestructuración y dispersión


¿Cuáles son los beneficios de usar los props.something construir sobre el something construir?

Así es como se ve sin usar la desestructuración:

 const Display = (props) => <div>{props.something}</div> 

Aquí es lo mismo, pero con el uso de la desestructuración:

 const Display = ({ something }) => <div>{something}</div> 

El uso de la desestructuración mejora la legibilidad del código. Pero esto no limita su impacto positivo en el proyecto. Al usar la desestructuración, el programador se ve obligado a tomar decisiones sobre qué recibe exactamente el componente y qué produce exactamente. Esto evita que cualquiera que tenga que editar el código de otra persona tenga que mirar cada línea del método de render en busca de todas las propiedades que utiliza el componente.

Además, este enfoque brinda una oportunidad útil para establecer valores de propiedad predeterminados. Esto se hace al comienzo del código del componente y elimina la necesidad de escribir código adicional en el cuerpo del componente:

 const Display = ({ something = 'apple' }) => <div>{something}</div> 

Es posible que haya visto algo como el siguiente ejemplo antes:

 const Display = (props) => ( <Agenda {...props}>   {' '}   //     Agenda   <h2><font color="#3AC1EF">Today is {props.date}</font></h2>   <hr />   <div>     <h3><font color="#3AC1EF">▍Here your list of todos:</font></h3>     {props.children}   </div> </Agenda> ) 

Tales construcciones no son fáciles de leer, pero este no es su único problema. Entonces, hay un error. Si la aplicación también muestra componentes secundarios, entonces props.children muestra en la pantalla dos veces. Si el trabajo en el proyecto se lleva a cabo en un equipo y los miembros del equipo no son lo suficientemente cuidadosos, la probabilidad de tales errores es bastante alta.

Si en cambio destruyes las propiedades, el código del componente será más claro y la probabilidad de errores disminuirá:

 const Display = ({ children, date, ...props }) => ( <Agenda {...props}>   {' '}   //     Agenda   <h2><font color="#3AC1EF">Today is {date}</font></h2>   <hr />   <div>     <h3><font color="#3AC1EF">▍Here your list of todos:</font></h3>     {children}   </div> </Agenda> ) 

Resumen


En este artículo, revisamos 12 recomendaciones para aquellos que están desarrollando aplicaciones React usando Redux. Esperamos que encuentre algo aquí que sea útil para usted.

Estimados lectores! ¿Qué consejos agregarías a los de este artículo?



Source: https://habr.com/ru/post/456336/


All Articles