11 dicas para usar o Redux ao desenvolver aplicativos React

Quando se trata de desenvolver aplicativos React, em termos de arquitetura de código, os projetos pequenos geralmente são mais flexíveis que os grandes. Não há nada de errado em criar esses projetos usando diretrizes práticas destinadas a aplicativos maiores. Mas tudo isso, no caso de pequenos projetos, pode ser simplesmente desnecessário. Quanto menor o aplicativo, mais "condescendente" se refere ao uso de soluções simples nele, possivelmente não ideais, mas que não exigem muito tempo para sua implementação.



Apesar disso, gostaria de observar que algumas das recomendações que serão dadas neste material visam a aplicações React de qualquer escala.

Se você nunca criou um aplicativo de produção, este artigo pode ajudá-lo a se preparar para o desenvolvimento de soluções em larga escala. Algo assim poderia muito bem se tornar um dos seus próximos projetos. A pior coisa que pode acontecer a um programador é quando ele trabalha em um projeto e percebe que precisa refatorar grandes quantidades de código para melhorar a escalabilidade e a capacidade de manutenção do aplicativo. Tudo parece ainda pior se não houvesse testes de unidade no projeto antes da refatoração.

O autor deste material pede ao leitor que aceite sua palavra. Ele esteve em situações semelhantes. Então, ele conseguiu várias tarefas que precisavam ser resolvidas em um determinado período de tempo. A princípio, ele pensou que tudo o que fazia era excelente. A fonte de tais pensamentos foi que seu aplicativo da web, depois de fazer alterações, continuou a funcionar e, ao mesmo tempo, continuou a funcionar rapidamente. Ele sabia como usar o Redux, como estabelecer a interação normal entre os componentes da interface do usuário. Pareceu-lhe que ele entendia profundamente os conceitos de redutores e ações. Ele se sentiu invulnerável.

Mas aqui o futuro surgiu.

Depois de alguns meses trabalhando no aplicativo, mais de 15 novos recursos foram adicionados a ele. Depois disso, o projeto ficou fora de controle. O código que usou a biblioteca Redux se tornou muito difícil de manter. Por que isso aconteceu? A princípio, não parecia que o projeto esperava uma vida longa e sem nuvens?

O autor do artigo diz que, ao fazer perguntas semelhantes, percebeu que havia plantado uma bomba-relógio no projeto com as próprias mãos.

A biblioteca Redux, se usada corretamente em grandes projetos, ajuda, à medida que esses projetos crescem, a manter seu código em um estado suportado.

Aqui estão 11 dicas para quem deseja desenvolver aplicativos React escaláveis ​​usando o Redux.

1. Não coloque o código de ação e constantes em um só lugar


Você pode encontrar alguns tutoriais de Redux nos quais constantes e todas as ações são colocadas no mesmo local. No entanto, essa abordagem, conforme o aplicativo cresce, pode levar rapidamente a problemas. As constantes precisam ser armazenadas separadamente, por exemplo, em ./src/constants . Como resultado, para procurar constantes, é necessário procurar apenas uma pasta e não várias.

Além disso, a criação de arquivos separados para armazenar ações parece completamente normal. Esses arquivos encapsulam ações diretamente relacionadas entre si. Ações em um único arquivo, por exemplo, podem ter semelhanças em termos de como e como são usadas.

Suponha que você esteja desenvolvendo um jogo de arcade ou role-playing e criando as classes warrior (guerreiro), sorceress (feiticeira) e archer (arqueiro). Em tal situação, você pode obter um alto nível de suporte ao código organizando as ações da seguinte maneira:

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

Será muito pior se tudo se encaixar em um arquivo:

 src/actions/classes.js 

Se o aplicativo se tornar muito grande, talvez seja ainda melhor usar aproximadamente a seguinte estrutura de divisão de código em arquivos:

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

Apenas um pequeno fragmento dessa estrutura é mostrado aqui. Se você pensa em usar essa abordagem de maneira mais ampla e consistente, você terá algo como este conjunto de arquivos:

 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 

Aqui está a aparência da ação do arquivo src/actions/sorceress/skills para o 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, }) 

Aqui está o conteúdo do 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 })   }) } } 

A razão pela qual organizamos o código dessa maneira é porque novos recursos são constantemente adicionados aos projetos. Isso significa que precisamos estar preparados para sua aparência e, ao mesmo tempo, nos esforçar para garantir que os arquivos não sejam sobrecarregados com código.

No início do trabalho no projeto, isso pode parecer desnecessário. Porém, quanto maior o projeto, mais forte será a força de tal abordagem.

2. Não coloque o código redutor em um só lugar


Quando vejo que o código dos meus redutores se transforma em algo semelhante ao mostrado abaixo, entendo que preciso mudar alguma coisa.

 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 } } 

Esse código, sem dúvida, poderia levar muito rapidamente a muita confusão. Portanto, é melhor manter a estrutura de trabalho com o estado da forma mais simples possível, visando o nível mínimo de aninhamento. Em vez disso, você pode tentar recorrer à composição de redutores.

Um truque útil no trabalho com redutores é criar um redutor de ordem superior que outros redutores geram. Leia mais sobre isso aqui.

3. Use nomes de variáveis ​​informativos


A nomeação de variáveis, à primeira vista, pode parecer uma tarefa elementar. Mas, de fato, essa tarefa pode ser uma das mais difíceis.

A seleção de nomes de variáveis ​​é geralmente relevante para diretrizes práticas para escrever código limpo. A razão pela qual existe um "nome de variável" em geral é porque esse aspecto do desenvolvimento de código desempenha um papel muito importante na prática. A seleção malsucedida de nomes de variáveis ​​é uma maneira de prejudicar você e os membros da sua equipe no futuro.

Você já tentou editar o código de outra pessoa e, ao mesmo tempo, encontrou dificuldades em entender o que exatamente esse código faz? Você já executou um programa estrangeiro e descobriu que ele não funciona como o esperado?

Eu argumentaria para provar que nesses casos você encontrou o chamado "código sujo".

Se você precisar lidar com código semelhante em aplicativos grandes, isso é apenas um pesadelo. Infelizmente, isso acontece com bastante frequência.

Aqui está um caso da vida. Editei o código do gancho React em um aplicativo e naquele momento eles me enviaram uma tarefa. Era para implementar no aplicativo a capacidade de exibir informações adicionais sobre os médicos. Esta informação deve ter sido mostrada ao paciente que clica na foto do perfil do médico. Era necessário retirá-lo da tabela, pois era necessário chegar ao cliente após o processamento da próxima solicitação ao servidor.

Esta não foi uma tarefa difícil, o principal problema que encontrei foi o fato de ter passado muito tempo descobrindo onde exatamente o que eu precisava estava localizado no código do projeto.

dataToSend no código as palavras info , dataToSend , dataObject e outras que, na minha dataObject , estão associadas aos dados recebidos do servidor. Após 5 a 10 minutos, consegui encontrar o código responsável por trabalhar com os dados necessários. O objeto no qual eles terminaram foi chamado paymentObject . Na minha opinião, um objeto relacionado a pagamentos pode conter algo como um código CVV, número do cartão de crédito, código postal do pagador e outras informações semelhantes. O objeto que descobri tinha 11 propriedades. Apenas três deles estavam relacionados a pagamentos: forma de pagamento, identificador de perfil de pagamento e uma lista de códigos de cupom.

A situação também não melhorou porque eu tive que fazer alterações nesse objeto necessárias para resolver a tarefa diante de mim.

Em resumo, é recomendável que você evite usar nomes obscuros para funções e variáveis. Aqui está um código de exemplo no qual o nome da função de notify não revela seu 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. Não altere estruturas ou tipos de dados em fluxos de dados de aplicativos já configurados


Um dos maiores erros que já cometi foi alterar a estrutura de dados em um fluxo de dados de aplicativos já configurado. A nova estrutura de dados traria um enorme aumento de desempenho, pois usava métodos rápidos para procurar dados em objetos armazenados na memória, em vez de iterar sobre matrizes. Mas já era tarde demais.

Peço que você não faça isso. Talvez algo como isso possa ser concedido apenas a alguém que sabe exatamente quais partes do aplicativo isso pode afetar.

Quais são as consequências de tal passo? Por exemplo, se algo foi primeiro uma matriz e depois se tornou um objeto, isso pode atrapalhar a operação de muitas partes do aplicativo. Cometi um grande erro ao acreditar que conseguia me lembrar de todos os lugares no código que poderiam ser afetados por uma alteração na apresentação de dados estruturados. No entanto, nesses casos, sempre há algum código afetado pela alteração e do qual ninguém se lembra.

5. Use trechos


Eu era fã do editor Atom, mas mudei para o VS Code devido ao fato de esse editor ser incrivelmente rápido em comparação ao Atom. E ele, em sua velocidade, suporta um grande número de possibilidades diferentes.

Se você também usa o VS Code, recomendo instalar a extensão de trechos de projetos . Essa extensão permite ao programador criar trechos personalizados para cada espaço de trabalho usado em um projeto. Essa extensão funciona da mesma maneira que o mecanismo Use Snippets, incorporado ao VS Code. A diferença é que, ao trabalhar com os snippets do projeto, a pasta .vscode/snippets/ é criada no projeto. Parece com a figura a seguir.


O conteúdo da pasta .vscode / snippets /

6. Crie testes unitários, de ponta a ponta e de integração


À medida que o tamanho do aplicativo aumenta, torna-se mais assustador para o programador editar código que não é coberto por testes. Por exemplo, pode acontecer que alguém tenha editado o código armazenado em src/x/y/z/ e decidido enviá-lo para produção. Se, ao mesmo tempo, as alterações feitas afetarem as partes do projeto em que o programador não pensou, tudo poderá terminar em um erro que um usuário real encontrará. Se houver testes no projeto, o programador saberá sobre o erro muito antes do código entrar em produção.

7. Brainstorm


Os programadores, no processo de introdução de novos recursos nos projetos, geralmente se recusam a debater. Isso acontece porque essa atividade não está relacionada à escrita de código. Isso acontece especialmente quando muito pouco tempo é alocado para a tarefa.

E por que, a propósito, você tem que debater durante o desenvolvimento de aplicativos?

O fato é que, quanto mais complexa a aplicação se torna, mais atenção os programadores devem prestar às partes individuais. O brainstorming ajuda a reduzir o tempo necessário para refatorar o código. Depois que eles são mantidos, o programador está armado com o conhecimento do que pode dar errado durante a conclusão do projeto. Frequentemente, os programadores, enquanto desenvolvem um aplicativo, nem se preocupam em pensar pelo menos um pouco sobre como fazer tudo da melhor maneira.

É por isso que o brainstorming é muito importante. Durante esse evento, o programador pode considerar a arquitetura do código, pensar em como fazer as alterações necessárias no programa, rastrear o ciclo de vida dessas alterações e criar uma estratégia para trabalhar com elas. Não vale a pena criar o hábito de manter todos os planos exclusivamente em sua própria cabeça. É isso que os programadores fazem, que são excessivamente confiantes. Mas lembrar de tudo absolutamente é simplesmente impossível. E, assim que algo for feito errado, os problemas aparecerão um após o outro. Este é o princípio dos dominós em ação.

O brainstorming também é útil em equipes. Por exemplo, se, no decorrer do trabalho, alguém se deparar com um problema, ele poderá recorrer aos materiais da sessão de brainstorming, pois o problema que surgiu com ele poderia muito bem já ter sido considerado. As anotações feitas durante a sessão de brainstorming podem muito bem desempenhar o papel de um plano para resolver o problema. Esse plano permite avaliar claramente a quantidade de trabalho realizado.

8. Crie modelos de aplicativos


Se você vai começar a desenvolver o aplicativo, precisa tomar uma decisão sobre como ele ficará e como os usuários irão interagir com ele. Isso significa que você precisará criar um layout de aplicativo. Você pode usar várias ferramentas para isso.

O Moqups é uma das ferramentas de maquete de aplicativos que eu sempre ouvi falar. Essa é uma ferramenta rápida criada usando HTML5 e JavaScript e não impõe requisitos especiais ao sistema.

Criar um aplicativo simulado simplifica e acelera muito o processo de desenvolvimento. O layout fornece ao desenvolvedor informações sobre o relacionamento entre as partes individuais do aplicativo e sobre que tipo de dados serão exibidos em suas páginas.

9. Planejar fluxo de dados em aplicativos


Quase todos os componentes do seu aplicativo serão associados a alguns dados. Alguns componentes usarão suas próprias fontes de dados, mas a maioria dos componentes recebe dados de entidades acima deles na hierarquia de componentes. Para as partes do aplicativo em que os mesmos dados são compartilhados por vários componentes, é útil fornecer algum armazenamento centralizado de informações localizado no nível superior da hierarquia. É nessas situações que a biblioteca Redux pode fornecer assistência inestimável ao desenvolvedor.

Eu recomendo que, enquanto estiver trabalhando no aplicativo, elabore um diagrama mostrando as maneiras pelas quais os dados são movidos nesse aplicativo. Isso ajudará na criação de um modelo de aplicativo claro. Além disso, estamos falando sobre o código e a percepção do aplicativo pelo programador. Esse modelo ajudará, além disso, na criação de redutores.

10. Use os recursos de acesso a dados


À medida que o tamanho do aplicativo aumenta, aumenta também o número de seus componentes. E quando o número de componentes aumenta, o mesmo acontece com a frequência do uso de seletores ( mapStateToProps -redux ^ v7.1) ou mapStateToProps . Suponha que você descubra que seus componentes ou ganchos geralmente acessam fragmentos de estado em diferentes partes do aplicativo usando uma construção como useSelector((state) => state.app.user.profile.demographics.languages.main) . Nesse caso, isso significa que você precisa pensar em criar funções de acesso a dados. Os arquivos com essas funções devem ser armazenados em um local público a partir do qual componentes e ganchos possam importá-los. Funções semelhantes podem ser filtros, analisadores ou qualquer outra função para transformação de dados.

Aqui estão alguns exemplos.

Por exemplo, src/accessors pode conter o seguinte código:

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

Aqui está a versão usando o connect , que pode ser localizado no caminho 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) 

Aqui está a versão que usa useSelector localizada em 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 

Além disso, esforce-se para garantir que essas funções sejam imutáveis, desprovidas de efeitos colaterais. Descubra por que eu dou essa recomendação aqui .

11. Controle o fluxo de dados nas propriedades usando a sintaxe de desestruturação e propagação


Quais são os benefícios de usar o construto props.something something sobre o constructo something ?

Aqui está o que parece sem usar a desestruturação:

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

Aqui está o mesmo, mas com o uso da desestruturação:

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

O uso da desestruturação melhora a legibilidade do código. Mas isso não limita seu impacto positivo no projeto. Usando a desestruturação, o programador é forçado a tomar decisões sobre o que exatamente o componente recebe e o que exatamente ele gera. Isso evita que qualquer pessoa que precise editar o código de outra pessoa precise examinar todas as linhas do método render em busca de todas as propriedades que o componente usa.

Além disso, essa abordagem fornece uma oportunidade útil para definir valores de propriedade padrão. Isso é feito no início do código do componente e elimina a necessidade de escrever código adicional no corpo do componente:

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

Você pode ter visto algo como o seguinte exemplo 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> ) 

Tais construções não são fáceis de ler, mas esse não é o único problema. Então, há um erro. Se o aplicativo também exibir componentes filhos, props.children exibido na tela duas vezes. Se o trabalho no projeto é realizado em uma equipe e os membros da equipe não são cuidadosos o suficiente, a probabilidade de tais erros é bastante alta.

Se você destruir as propriedades, o código do componente será mais claro e a probabilidade de erros diminuirá:

 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> ) 

Sumário


Neste artigo, analisamos 12 recomendações para quem está desenvolvendo aplicativos React usando o Redux. Esperamos que você encontre aqui algo que seja útil para você.

Caros leitores! Que dicas você adicionaria às deste artigo?



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


All Articles