Escrevendo aplicativos de reações escaláveis ​​com o padrão de pasta de componente

Descubra como organizar seus componentes do React usando o padrão de pasta do componente . Isso ajudará a desordenar seus projetos e sua vida. Logo será seu novo melhor amigo.

Foto de Wesley Tingey em Unsplash

Nós todos os vimos. Os gigantes gigantes dos componentes de deus da linha 400+. Eles podem ser tão grandes, que leva a melhor parte de um dia para obter uma compreensão de alto nível sobre o que eles fazem.

Neste artigo, tomamos um desses componentes de deus e dividi-lo em unidades de trabalho de tamanho e lógica mordidas. Em seguida, aplicaremos o padrão de pasta do componente para compartimentar as peças.

Pesquisa Giphy

Para o nosso exemplo, vou usar um aplicativo de pesquisa Giphy cuja estrutura de arquivos se parece com isso.

 src / 
| - App.js
| - GiphySearch.js

Parece bastante simples, mas tudo (estado, visualização e carregamento de dados) estão todos em um único componente. Isso torna o GiphySearch difícil de manter.

O aplicativo de pesquisa anterior do Giphy no CodeSandbox.io . Funciona, mas não é bonito.

Você pode começar a imaginar os subcomponentes que precisaremos. Primeiro precisamos de um controlador que mantenha o estado (por exemplo, loading , error e data ). Nós também precisamos:

  • Um componente SearchInput que processa a caixa de entrada e chama um manipulador onSearch quando o usuário clica em "Pesquisar".
  • A Loading componente que exibe um indicador de carregamento.
  • Um módulo loadData que envia uma consulta de pesquisa para a API Giphy e obtém o URL da imagem.
  • Uma Image componente que exibe o URL da imagem.
  • Um componente de Error no caso de algo dar errado.

Vou adicionar mais um componente, um componente de View que distribui seletivamente para os outros componentes de renderização. Nosso sistema de arquivos agora seria algo assim.

 src / 
| - App.js
| - GiphyView.js
| - GiphySearchInput.js
| - GiphyImage.js
| - GiphyLoading.js
| - GiphyError.js
| - giphyLoadData.js

Um grande problema com esta abordagem é que ao adicionar componentes, nossa pasta src começará a preencher rapidamente. Haverá muitos arquivos, tornando difícil encontrar o que você está procurando. Mas para mim, o problema é que estamos usando uma estrutura de arquivos com nomes de nomes. Adivinha? Esse é exatamente o problema que as pastas resolvem.

Apresentando o padrão de pasta de componentes

O que podemos fazer para estruturar logicamente todos esses arquivos? E se colocarmos nossos arquivos relacionados em uma única pasta GiphySearch ? Isto é o que fazemos para o desenvolvimento web tradicional com um index.html e seus arquivos de suporte. Pareceria assim.

 src / 
| - App.js
| - GiphySearch /
| - index.js
| - View.js
| - SearchInput.js
| - Image.js
| - Loading.js
| - Error.js
| - loadData.js

Bom, mas qual é o arquivo index.js que está fazendo aqui e o que ele faz? Bem note que o nosso arquivo GiphySearch.js já está desaparecido. Nós simplesmente renomeamos index.js .

O que eu gosto sobre essa abordagem é que, uma vez que você tenha seu componente GiphySearch trabalhando, colapse a pasta. Você não estará mais olhando para todos esses arquivos, reduzindo a confusão visual. Pesquisadores do Instituto de Neurociências da Universidade de Princeton realizaram um estudo que sugere reduzir a confusão pode ajudá-lo a manter o foco.

 src / 
| - App.js
| - GiphySearch /

Ahh … muito melhor. Parece muito com a estrutura de arquivos com a qual começamos, não é?

Alguns de vocês estão perguntando: "Eu vou ter que mudar minhas declarações de import com esta nova estrutura de arquivos? Afinal, o código para Giphy Search está dentro de uma pasta e em um arquivo com um nome diferente. "A resposta é … NÃO! Essa é a beleza do padrão de pasta do componente.

Seu aplicativo ainda parece algo assim, não importa se o módulo vive em GiphySearch.js ou GiphySearch/index.js . Viva a ciência!

 importar GiphySearch de './ GiphySearch '; 
 const App = () => ( 
<GiphySearch inicialQuery = "cão" />
);

Antes de mergulhar e olhar para o código linha por linha, aqui está o aspecto da nossa nova pesquisa Giphy.

A versão posterior , usando o SpinLoad injetado. Experimente e veja o código em CodeSandbox.io

Quebrando para baixo

Vejamos nosso componente GiphySearch completo e o que está dentro de cada arquivo. Vamos começar com index.js . O único motivo para a existência é manter o estado. Nada mais.

 importar Vista de './View'; 
importar dados de carregamento de './loadData';
 A classe padrão de exportação se estende Componente { 
state = {};
load = this.load.bind (this);
 carga assíncrono (... args) { 
experimentar {
this.setState ({loading: true, error: false});
const data = aguardar loadData (... args);
this.setState ({loading: false, data});
} catch (ex) {
this.setState ({loading: false, error: true});
}
}
 render () { 
Retorna (
<Visualizar {... this.props} {... this.state} onLoad = {this.load} />
);
}
}

Ambos props , state e uma chamada de retorno onLoad são transmitidos para o componente View . A forma como obtemos os data é tratada pelo módulo loadData . Isso ocorre porque nem tem nada a ver com o estado.

De fato, olhe de perto. Nada sobre este componente tem nada a ver com uma pesquisa da Giphy. É abstrato em todas as formas possíveis. Algo que você poderia usar uma e outra vez. IMO, isso é uma elegância simplista.

"Um componente deve fazer uma coisa e fazê-lo bem"

Próximo é o componente View . Ele determinará o que renderizar. É realmente mais um controlador de exibição, como ele passa junto a outros componentes de renderização para fazer a renderização real – tudo com base no estado passado como adereços do index.js .

 importe SearchInput de './SearchInput'; 
Importar imagem de './Image';
importar carregando de './Loading';
Importar erro de './Error';
 const View = ({ 
carregando, erro, dados, inicialQuery, onLoad,
RenderSearchInput, RenderImage, RenderLoading, RenderError,
}) => (
<div>
<RenderSearchInput
InitialQuery = {initialQuery}
onSearch = {onLoad}
/>
<section>
{Faz {
se (carregando) {
<RenderLoading />
} else if (error) {
<RenderError error = {error} />
} outro {
<RenderImage src = {data} />
}
}}
</ section>
</ div>
);
 View.propTypes = { 
...
};
 View.defaultProps = { 
RenderSearchInput: SearchInput,
RenderImage: Imagem,
RenderLoading: Carregando,
RenderError: Erro,
};

Observe que usamos a injeção de componentes para renderizar com base nas condições de loading , error e data ( RenderLoading , RenderError e RenderImage respectivamente). Os padrões são fornecidos se nenhum for especificado.

Em seguida, está o arquivo loadGiphy . Ele usa a fetch para atingir o ponto final REST de Giphy, converte o JSON em um objeto e extrai o URL da imagem. É bastante padrão, então não vou mostrar isso aqui.

O resto dos nossos componentes de renderização, Loading , Error e Image são simples componentes funcionais sem estado, por isso também não desperdiçarei as polegadas das colunas. No entanto, o código-fonte completo (bem como um exemplo em execução ao vivo) pode ser encontrado em CodeSandbox.

Padrão de implementação de referência

Existe uma técnica mais que eu gostaria de salientar. Sempre que você usa um retorno de retorno para executar sua renderização – seja ela uma Função como Criança ( não se atreva! ), Função como Prop (também conhecido como render prop) ou injeção de componentes (o que uso quase que exclusivamente) – pode às vezes seja vantajoso fornecer um padrão se nenhum for especificado. Refiro-me a isso como o padrão de implementação de referência .

Isso permite que o consumidor do seu componente passe opcionalmente um componente de renderização. No entanto, se não o fizerem, algum tipo de componente padrão ou "referência" executará a renderização.

Vamos substituir o componente Loading e torná-lo um pouco mais agradável do que a implementação de referência padrão passando o suporte RenderLoading .

 importar GiphySearch de './GiphySearch'; 
importe SpinLoad de './SpinLoad';
 const App = () => ( 
<div>
<h1> Giphy Pesquisa </ h1>
<GiphySearch inicialQuery = "cão" RenderLoading = {SpinLoad} />
</ div>
);

Observe que não estamos substituindo os RenderSearchInput , RenderImage ou RenderError . Eles usarão as implementações de referência padrão.

Aqui está o que o componente SpinLoad , construído usando os componentes de estilo , parece.

 importar estilo, {keyframes} de 'styled-components'; 
 const rotate360 = keyframes` 
a partir de {
transformar: rotear (0);
}
para {
transformar: rotear (360deg);
}
`;
 const Spinner = styled.div` 
cor: # 333;
font-size: 18px;
font-family: sans-serif;
 &:antes { 
exibição: bloco em linha;
conteúdo: '';
largura: 18px;
altura: 18px;
radio de fronteira: 50%;
borda: sólido 2px #ccc;
border-bottom-color: # 66c;
animação: $ {rotate360} 1s infinito linear;
margem direita: 6px;
vertical-align: bottom;
}
`;
 const Carregando = () => ( 
<Spinner> Carregando </ Spinner>
);
 exportação padrão Carregamento; 

Embora o padrão de implementação de referência nem sempre tenha sentido em seu componente, é um padrão poderoso para estar ciente. Isso pode ajudar com o teste de seu componente, pois você pode injetar uma implementação de teste.

Conclusão

Espero que você tenha a oportunidade de seguir as disciplinas e os padrões que você leu hoje e aplicá-los ao seu próprio código. O padrão da pasta do componente descompacta seu projeto, enquanto o padrão de implementação de referência permite que você forneça padrões razoáveis ​​para renderizar retorno de chamada . Ambos são meus companheiros fiéis ao escrever aplicativos de Reacção.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *