Boas práticas ao escrever código em React js
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!
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?
- Formação React com JavaScript
- Formação React com Typescript
- Formação React: consumindo API’s
- Formação React: Gestão de Estados
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!