Boas práticas ao escrever código em React js

Boas práticas ao escrever código em React js
Neilton Seguins
Neilton Seguins

Compartilhe

Introdução

Para quem está começando a desenvolver em React JS, é muito comum surgirem dúvidas do tipo:

  • Como eu organizo minhas pastas e arquivos?
  • Como nomeio meus componentes?
  • Como organizo a importação de arquivos?
  • Neste artigo mostro as práticas mais recomendadas, para você aplicar enquanto escreve seus código com React js.

Vem comigo!

Imersão dev Back-end: mergulhe em programação hoje, com a Alura e o Google Gemini. Domine o desenvolvimento back-end e crie o seu primeiro projeto com Node.js na prática. O evento é 100% gratuito e com certificado de participação. O período de inscrição vai de 18 de novembro de 2024 a 22 de novembro de 2024. Inscreva-se já!

Guia, não regra

Antes de começarmos, é fundamental que você entenda que este é apenas um guia e não um conjunto de regras que devem ser seguidas religiosamente em todos os seus projetos. Cada time de desenvolvimento pode estabelecer regras próprias que devem ser seguidas por cada membro do time para facilitar a comunicação, leitura e interpretação do código. Então isso pode variar.

Se você está pensando em adotar padrões de boas práticas universalmente aceitos, este guia é para você. Ele foi escrito pensando nas boas práticas que são adotadas pela maioria das pessoas que escrevem códigos em React, inclusive se baseando em guias de estilos de grandes empresas como a Google e Airbnb.

Práticas recomendadas

Arquivos JSX

Inclua apenas um componente React por arquivo e sempre use a sintaxe .jsx. O JSX é uma extensão da linguagem JavaScript que é usada pelo React para criar interfaces de usuário. Com ele, você consegue misturar código JavaScript com sintaxe de HTML, o que permite escrever componentes de interface de usuário em um único arquivo, tornando a experiência de quem escreve muito melhor e mais fácil. Leia a documentação para saber mais sobre o JSX.

Nomenclaturas

É uma convenção utilizar PascalCase para nomenclatura de arquivos, por exemplo, ModalCompra.jsx. Então, sempre que puder use PascalCase para componentes React e camelCase para suas instâncias.

Exemplo:

// evite
import modalCompra from './ModalCompra'

// prefira
import ModalCompra from './ModalCompra'

// evite
const ModalCompra = < ModalCompra />

// prefira 
const modalCompra = < ModalCompra />

Também é recomendado usar o nome do arquivo como o nome do componente. Por exemplo, ModalCompra.jsx deve ter um nome de referência ModalCompra. Apenas em casos em que o arquivo é o arquivo raíz de um diretório, use o nome de referência como index.jsx e o nome do diretório como arquivo de referência.

// evite
import ModalCompra from './ModalCompra/ModalCompra'

// evite
import ModalCompra from './ModalCompra/index'

Na prática, é comum termos uma pasta com o nome do componente e um arquivo index.jsx exportando por padrão o componente. Isso torna muito mais fácil e legível a importação de arquivos internos.

// prefira
import ModalCompra from './ModalCompra'

E nesse caso não é obrigatório utilizar o nome do arquivo no caminho de importação, ou seja, o index.jsx, assim como não precisamos explicitar a extensão do arquivo, já que sua IDE provavelmente irá interpretar corretamente.

Class Components x Function Components

No React temos duas maneiras de criarmos componentes, utilizando classes ou funções. Os componentes de classes são mais complexos e necessitam de uma certa habilidade em POO (Programação orientada a Objetos) e acabou se tornando obsoleta, principalmente por conta de sua complexidade. Um componente que é uma função é mais simples e aceita receber props como parâmetros além de retornar um elemento JSX.

Prefira utilizar componentes funcionais por possuírem uma sintaxe mais simples, podendo ter menos código mas sem perder a legibilidade. Contudo, se você precisa criar uma tarefa mais complexa como lidar com alguma lógica, gerenciar estados ou manipular eventos, isto é, features que possam não ser possíveis de executar em componentes funcionais, então você deve optar por usar class components.

// use assim se você quiser executar tarefas mais completas ou lógica que não é possível em componentes funcionais

class Contador extends React.Component{
       state = {
         contador:0
       }

       constructor(props){
          super(props)
          this.handleClick = this.handleClick.bind(this)
       }

       handleClick(){
          this.setState({contador: this.state.contador + 1})
       }

       render(){
          return (
             <div>
                 <p>
              contador: {this.state.contador}
                 </p>
                 <button onClick={this.handleClick}>
                      Incrementar
                 </button>
            </div>
          )
       }
}
// use assim sempre que seu componente for simples, não requer lidar com lógica mais complicada

function Contador(){
    const [contador, setContador] = useState(0)
    const handleClick = ()=>setContador(contador +1)

    return (
        <div>
            <p>
                contador: {contador}
            </p>
            <button onClick={handleClick}>
                Incrementar
            </button>
        </div>
    )
}

Fragments

Ao precisar de um elemento pai para seus componentes, utilize fragments ao invés de div’s em seu código. Mas fique atento, os fragments só foram introduzidos a partir da versão 16.2. Então se você escreve código React em versões anteriores a essa melhor dá uma lida neste artigo da documentação.

Além de sujar o seu código com uma sopa de div’s, você acaba perdendo desempenho, pois estará criando nós extras no seu DOM Virtual do React. Por isso, prefira utilizar fragments, pois eles não sujam seu código e ajudam no desempenho da aplicação. Leia a documentação do React sobre os fragments neste link.

Exemplo:

// evite usar div’s apenas para agrupar todos os elementos dentro de um único elemento pai

return (
  <div>
    <Button />
    <SelectInput />
  </div>  
)
// prefira usar fragments

return (
  <>
    <Button />
    <SelectInput />
  </>  
)
// ou, em sua sintaxe completa:

return (
  <React.Fragment>
    <Button />
    <SelectInput />
  </React.Fragment>  
)

Props

Quando trabalhamos com props, estamos o tempo todo passando elas para componentes filhos, e nesses componentes filhos recebendo-as como parâmetros, podendo então utilizá-las nesses componentes.

As props são objetos, logo, podemos utilizar recursos da linguagem JavaScript para trabalhar com elas. Então utilize a desestruturação sempre que possível ao receber props em seus componentes React.

Exemplo:

// evite usar assim 

function Button(props){
    return (
        <button onClick={props.onClick}>
            {props.text}
        </button>
    )
}
// prefira usar assim

function Button(props){
    const {onClick, text} = props
    return (
        <button onClick={onClick}>
            {text}
        </button>
    )
}

// ou melhor, utilize assim

function Button({onClick, text}){
     return (
        <button onClick={onClick}>
            {text}
        </button>
    )
}

Se temos muitas props que precisamos passar para um mesmo componente, e passarmos uma a uma, este componente consequentemente irá ser renderizado mais vezes. Então é recomendado que você dê menos motivos para um componente sofrer uma renderização desnecessária, ou seja, passe menos props únicas para um componente se possível.

Mas e se eu precisar passar várias props para um componente? O que fazer?

Crie um objeto com as props conhecidas e com valores explícitos e espalhe este objeto no componente que irá recebê-las.

Exemplo:

// evite usar assim

export default function Artigo {
    const props = {
      texto: '',
      estaPublicado: false
    }

    return (<div texto={props.texto} estaPublicado={props.estaPublicado} />);
  }
// prefira usar assim  

export default function Artigo {
    const props = {
      texto: '',
      estaPublicado: false
    }

    return (<div {props} />);
}

// ou, utilize assim

export default function Artigo {
    const props = {
      texto: '',
      estaPublicado: false
    }

    return (<div {...props} />);
}

Funções auxiliares

Quando definimos uma função para nos auxiliar a executar alguma lógica em nossos componentes onde devemos declará-las?

O recomendado é que essas funções auxiliares sejam declaradas fora do componente, em um hook personalizado, por exemplo, ou até mesmo dentro do arquivo do componente mas de forma separada. Isso está ligado ao Princípio de Responsabilidade Única (SRP), um dos fundamentos do SOLID, que diz que:

Uma classe deve ter apenas uma responsabilidade, ou um motivo para mudar.

Extrapolando esse conceito para o React podemos dizer que:

Uma função, módulo ou componente deve fazer exatamente uma única coisa.

Então a lógica deste componente não deve se misturar com a lógica de funções auxiliares e deve ser evitada sempre que possível. Por isso, você pode pensar em isolar estas funções em hooks customizados, ou em último caso, declarar estas funções auxiliares fora do escopo da função do componente.

Exemplo:

// evite escrever assim

function ComponenteTexto(props){
    function toText(value){
        return String(value)
    }

    return (
        <div>
            {toText(props.data)}
        </div>
    )
}
// prefira usar assim
function useText(){
    function toText(value){
        return Number(value)
    }

    return {toText}
}

function ComponenteTexto(props){
    const {toText} = useText()

    return (
        <div>
            {toText(props.data)}
        </div>
    )
}

Mas cuidado! Hooks precisam seguir regras e você precisa conhecê-las! A principal e mais importante regra que você deve saber neste momento é que nunca deve chamar hooks dentro de loops (while, for), condições (if, else) e funções aninhadas. Os hooks precisam ser chamados no nível superior do seu componente React. Isso garante que os hooks sejam chamados na mesma ordem sempre que um componente React for renderizado.

Renderização condicional

Quando queremos renderizar algo somente se uma condição for verdadeira, em alguns casos é recomendado utilizar o operador && para isso. Se você não conhece este operador para renderizar elementos condicionalmente recomendo que você dê uma espiadinha na documentação do React.

Exemplo:

const UserList = ({ isLoading, result }) => (
   <div>
     {isLoading && <span>Loading...</span>}

     {!isLoading && (
       <ul>
         {result.map((user) => (
           <li key={user.id}>{user.name}</li>
         ))}
       </ul>
     )}
   </div>
);

Porém, quando precisamos renderizar uma coisa quando essa condição for verdadeira e outra quando essa condição for falsa, neste caso é recomendado o uso do operador ternário.

Exemplo:

const UserList = ({ isLoading, result }) => (
   <div>
     {isLoading ? (
         <span>Loading...</span>
     ) : (
         <ul>
             {result.map((user) => (
                  <li key={user.id}>{user.name}</li>
             ))}
          </ul>
     )}
   </div>
);

Nestes dois casos, você escolhe o estilo de realizar a renderização condicional conforme você e o seu time acharem mais apropriados, mas atente-se caso as condições se tornem muito complexas, talvez seja melhor adotar outras estratégias.

Ternários aninhados

Quando queremos renderizar componentes de forma condicional, já vimos que em alguns casos é recomendado utilizar operadores ternários. Porém se as condições se tornam complexas e você começa a aninhar estes operadores torna-se difícil ler o seu código, então a dica é evitar operadores ternários aninhados.

Para fugir de ternários aninhados você pode optar por utilizar tomadas de decisão para melhorar a legibilidade do código:

Exemplo:

// evite escrever assim

function List(){
    const {data} = useList()

    return data ? (
        data.length === 0 ? (
            <p>Nenhum elemento disponível</p>
        ) : (
            <>
                {data.map(({ nome }, indice) => (
                    <p key={indice}>{nome}</p>
                ))}
            </>
        )
    ) : (
        < Loading />
    );
}
// prefira escrever assim

function Callback({data, render}){
    if(data){
        if(data.length === 0){
            return <p>"Nenhum elemento disponível"</p>
        }

        return render(data)
    }

    return < Loading />
}

function List(){
    const {data} = useList()

    <Callback 
        data={data}
        render={(data)=>(
            <>
                {data.map(({ nome }, indice) => (
                    <p key={indice}>{nome}</p>
                ))}
            </>
        )}
    />
}

Mapeando listas em componentes

No exemplo acima utilizamos um map() para listar os elementos que vieram do useList(). Fazer uma interação sobre elementos de listas é um trabalho que realizamos com frequência quando trabalhamos em React. Por isso, para evitar problemas com ilegibilidade no código é recomendado isolar este mapeamento em um componente à parte.

Exemplo:

// se possível, evite

function List(){
    const {data} = useList()

    <Callback 
        data={data}
        render={(data)=>(
            <>
                {data.map(({ nome }, indice) => (
                    <p key={indice}>{nome}</p>
                ))}
            </>
        )}
    />
}
// se possível, faça

function List(){
    const {data} = useList()

    <Callback 
        data={data}
        render={(data)=>(
            < User data={data}/> 
        )}
    />
}

function User(){
    return (
        <>
            {data.map(({ nome }, indice) => (
                <p key={indice}>{nome}</p>
            ))}
        </>
    )
}

Organização de importações

Em aplicações com vários componentes, você pode acabar precisando importar todos eles em um único arquivo e ter algo deste tipo:

// paginas/Inicio/Inicio.jsx

import React from 'react';
import Cabecalho from 'componentes/Cabecalho';
import Menu from 'componentes/Menu';
import Cartao from 'componentes/Cartao';
import Botao from 'componentes/Botao';
import Rodape from 'componentes/Rodape';
import Modal from 'componentes/Modal';

Para contornar isso, você pode criar um arquivo index.js dentro da pasta componentes e exportar todos os componentes de lá.

// componentes/index.js
import Cabecalho from 'componentes/Cabecalho';
import Menu from 'componentes/Menu';
import Cartao from 'componentes/Cartao';
import Botao from 'componentes/Botao';
import Rodape from 'componentes/Rodape';
import Modal from 'componentes/Modal';

export { Cabecalho, Menu, Cartao, Botao, Rodape, Modal };

Assim, no componente que você precisar importar todos esses outros componentes, você só precisará escrever uma linha.

// paginas/Inicio/Inicio.jsx

import { Cabecalho, Menu, Cartao, Botao, Rodape, Modal } from 'componentes';

Bem mais simples e limpo, não é mesmo?

Conclusão

Ufa! Quantas dicas legais, não é mesmo? Espero que você tenha pegado todas elas. Essas são algumas boas práticas para que você possa aplicar em seus projetos e criar aplicações com muito menos linhas de código, bem mais organizada e sem perder legibilidade.

Ficou com vontade de aplicar essas boas práticas em seus projetos? Que tal checar as nossas formações em React e começar a colocar em prática o que acabou de aprender?

Se curtiu o artigo, não esqueça de compartilhar com seu time, com amigos e colegas próximos. E marca os perfis da Alura nas redes sociais utilizando a hashtag #AprendinoBlogdaAlura.

Até a próxima!

Neilton Seguins
Neilton Seguins

Sou graduado como Bacharel em Ciência e Tecnologia e em Engenharia Mecânica. Atuo como Instrutor de Desenvolvedor de Software na Alura e possuo experiência com desenvolvimento usando JavaScript/TypeScript, React js, Next js e Node.js. Amo compartilhar conhecimento, pois acredito que a educação é transformadora e quero mudar a vida de pessoas através da educação assim como consegui mudar de vida. Também amo ouvir e tocar música, ler livros e mangás e assistir séries.

Veja outros artigos sobre Front-end