Aumentando a legibilidade do código

English Article

A maioria das dicas apresentadas neste artigo dependerá do ES6 (ECMAScript 2015) sintaxe

Desestruturando

A desestruturação pode ser útil para retirar itens individuais de um objeto ou array. Tenha cuidado ao usar com arrays, pois você pode se confundir facilmente renomeando alguma propriedade de maneira errada, vamos dar uma olhada em um exemplo:


_14
// alunos => [idade, série]
_14
const alunos = [
_14
[19, 80.9],
_14
[21, 75.2],
_14
[19, 88.7]
_14
]
_14
_14
alunos.forEach(([idade]) => idade) // 19, 21, 19
_14
_14
// Mesmo renomeando o primeiro valor como nota, obtemos a idade
_14
alunos.forEach(([série, idade]) => série) // 19, 21, 19
_14
_14
// Podemos pular valores de uma array usando uma vírgula (,)
_14
alunos.forEach(([, nota]) => nota) // 80.9, 75.2, 88.7

Agora usando um objeto.


_14
const alunos = {
_14
Kevin: {
_14
idade: 19,
_14
nota: 80,9,
_14
},
_14
Laura: {
_14
idade: 21,
_14
nota: 75,2,
_14
},
_14
};
_14
_14
const { Kevin } = alunos;
_14
_14
Kevin.nota; // 80.9

Usando acessadores de propriedade

Podemos ter acesso a uma propriedade de duas maneiras, você pode usar o simples ponto ., ou a notação de acesso colchete [].


_6
const Kevin = {
_6
idade: 19,
_6
nota: 80,9
_6
}
_6
_6
Kevin['nota']) // 80.9

Um exemplo com CSS Modules: usando um nome de classe com caracteres especiais.


_3
const Texto = () => {
_3
return <p className={styles['primary-text']}>Olá!</p>
_3
}

Se desejar, você também pode acessar as propriedades dinamicamente:


_9
const mensagensDeStatus = {
_9
ERRO: 'servidor morreu',
_9
CARREGANDO: 'Estamos dando o nosso melhor',
_9
OCIOSO: 'Aguardando entrada'
_9
}
_9
_9
const status = getCurrentStatus() // CARREGANDO
_9
_9
mensagensDeStatus[status] // Estamos dando o nosso melhor

Usando parâmetros de função de objeto

Isso é situacional, mas geralmente quando tenho mais de 2 ou 3 parâmetros em uma função, uso esse método. Compare as funções abaixo:


_12
const retorneListaRandomica = (tamanhoDaLista, variacaoDeConteudo, delayEmMS) =>
_12
nova Promessa((resolve) => {
_12
const numeroRandomico = () => Math.ceil(Math.random() * variacaoDeConteudo);
_12
_12
const arrayGerada = Array(tamanhoDaLista)
_12
.fill(0)
_12
.map((_) => numeroRandomico());
_12
_12
setTimeout(() => resolve(generatedArray), delayEmMS);
_12
});
_12
_12
retorneListaRandomica(8, 5, 2000).then((array) => array); // [4, 4, 5, 1, 4, 5, 1, 4]

Desculpe, não consegui pensar em nada melhor do que isso. Mas vamos agora dar uma olhada nos parâmetros em objetos.


_4
const retorneListaRandomica = ({ tamanhoDaLista, variacaoDeConteudo, delayEmMS }) =>
_4
new Promise((resolve) => ... );
_4
_4
retorneListaRandomica({ tamanhoDaLista: 8, delayEmMS: 2000, variacaoDeConteudo: 5 });

Agora, não nos perdemos em nossos parâmetros e não precisamos nos preocupar em ordená-los corretamente.

Dicionários

Às vezes, queremos traduzir alguns retornos ou receber uma mensagem por status. Os dicionários podem fazer isso facilmente sem escrever um monte de if statements. Eles foram mencionados anteriormente em um bloco de código antes, então vamos retroceder.


_30
const ifStatement = (status) => {
_30
if (status === 'ERRO') return 'servidor morreu';
_30
if (status === 'CARREGANDO') return 'Estamos tentando o nosso melhor';
_30
if (status === 'OCIOSO') return 'Aguardando entrada';
_30
};
_30
_30
ifStatement('ERRO');
_30
_30
const switchCases = (status) => {
_30
switch (status) {
_30
case 'ERRO':
_30
return 'servidor morreu';
_30
case 'CARREGANDO':
_30
return 'Estamos dando o nosso melhor';
_30
case 'OCIOSO':
_30
return 'Aguardando entrada';
_30
default:
_30
throw new Error(`Status: ${status}, não encontrado`);
_30
}
_30
};
_30
_30
switchCases('ERRO');
_30
_30
dicionário const = {
_30
ERRO: 'servidor morreu',
_30
CARREGANDO: 'Estamos dando o nosso melhor',
_30
OCIOSO: 'Aguardando entrada',
_30
};
_30
_30
dicionário['ERRO'];

Arrow functions

As arrow functions têm suas desvantagens quando comparadas com as normais, mas você pode obter um bom resultado usando-as. Veja algumas comparações abaixo.


_16
// Versão normal
_16
function dobrarValorF(valor) {
_16
return value * 2
_16
}
_16
_16
function retorneNomeDePessoasF(pessoas) {
_16
return pessoas.map((pessoa) => pessoa.nome)
_16
}
_16
_16
// Versão Arrow function
_16
const dobrarValorA = (valor) => valor * 2
_16
_16
const retorneNomeDePessoasA = (pessoas) => pessoas.map((pessoa) => pessoa.nome)
_16
_16
// Desestruturando
_16
const retorneNomeDePessoasA = (pessoas) => pessoas.map(({ nome }) => nome)

Usando-os, podemos fazer tudo em apenas uma linha e omitir os parênteses em torno dos parâmetros caso haja apenas um. Na linha 27, combinamos a desestruturação com arrow functions.

Guard clauses - Evitando o else

Quando usamos if statements, pretendemos cobrir todos os casos de que precisamos, consequentemente inundando nosso código com o else. Vamos pegar este exemplo que usa o return:


_9
const verificarIdade = (idade) => {
_9
if (idade < 18) {
_9
return 'Não pode entrar na festa.'
_9
} else if (idade >= 18 && idade < 21) {
_9
return 'Pode entrar, mas não pode beber.'
_9
} else {
_9
return 'Pode entrar e beber.'
_9
}
_9
}

Na função verificarIdade verificamos o input, e a lógica funciona assim:

Temos alguns problemas neste código que podemos corrigir:

Vamos refatorar e conferir.


_6
const verificarIdade = (idade) => {
_6
if (idade < 18) return 'Não pode entrar na festa.'
_6
if (idade < 21) return 'Pode entrar, mas não pode beber.'
_6
_6
return 'Pode entrar e beber.'
_6
}

Veja como podemos usar as formas linha 2 ou linha 3-4 para retornar a mensagem, então use o que parecer melhor para você. Pessoalmente, para mensagens longas, uso o segundo método. Em conclusão, avalie se você precisa usar ou não uma keyword.

Quando não podemos evitar a keyword else

Normalmente, não podemos evitar o uso de else se precisarmos retornar a mesma coisa para ambas as condições.


_6
const verificarCargos = (usuário) => {
_6
if (user.isPremium) renderizarStatusPremium()
_6
else renderizarAnuncios()
_6
_6
return renderizarInterfaceDoAplicativo()
_6
}

Operador Spread para passar props no React

Este operador é responsável por obter o restante de um objeto, vamos supor que precisamos mapear uma lista de usuários e retornar um componente de cada usuário. Esta é a lista de usuários:


_10
const usuarios = [
_10
{
_10
nome: "Jorge",
_10
idade: 72,
_10
peso: 80,
_10
altura: 182,
_10
trabalho: "Caminhoneiro"
_10
},
_10
...
_10
]

E agora o componente e os props.

Sem operador de spread:


_6
const UserCard = ({ nome, idade, trabalho }) => <></>
_6
_6
const app = () =>
_6
users.map(({ nome, idade, trabalho }) => (
_6
<UserCard nome={nome} idade={idade} trabalho={emprego} />
_6
))

Usando o operador spread:


_1
const App = () => users.map((user) => <UserCard {...user} />)

Veja como sem precisar de todos os props, ainda podemos passar o operador spread para o componente. Mesmo desestruturando as props no primeiro exemplo, nosso segundo bloco de código dá muito mais legibilidade.

Short Circuit Evaluation

Às vezes, precisamos apenas de uma condicional curta para definir um valor de variável ou verificar uma condicional e, com o if statements gastamos muito espaço para coisas simples. Veja estes dois exemplos:


_6
// declaração if
_6
if (estaEnsolarado) roupaEscolhida = RoupaDeVerao
_6
else roupaEscolhida = RoupaDeInverno
_6
_6
// Short Circuits
_6
const roupaEscolhida = estaEnsolarado ? RoupaDeVerao : RoupaDeInverno



_5
...
_5
_5
if (estaEnsolarado && estouFeliz) return irParaPraia()
_5
_5
...

Para obter mais informações sobre os Short Circuits, verifique os recursos adicionais no final do artigo.

Evite Short Circuits no React

Sim, eu sei que recomendei o uso de Short Circuits neste artigo, mas às vezes evitá-los pode nos dar uma melhor legibilidade em nosso código. Vejamos alguns exemplos

Short Circuits


_17
const componente = ({ isAdmin }) => {
_17
const [state, setState] = useState(false)
_17
_17
return isAdmin ? (
_17
<AdminInterface>
_17
<>
_17
<></>
_17
</>
_17
</AdminInterface>
_17
) : (
_17
<UserInterface>
_17
<>
_17
<></>
_17
</>
_17
</UserInterface>
_17
)
_17
}

Declarações if


_20
const componente = ({ isAdmin }) => {
_20
const [state, setState] = useState(false)
_20
_20
if (éAdmin)
_20
return (
_20
<AdminInterface>
_20
<>
_20
<></>
_20
</>
_20
</AdminInterface>
_20
)
_20
_20
return (
_20
<UserInterface>
_20
<>
_20
<></>
_20
</>
_20
</UserInterface>
_20
)
_20
}

Componentes individuais


_7
const componente = ({ isAdmin }) => {
_7
const [state, setState] = useState(false)
_7
_7
if (isAdmin) return <AdminInterface />
_7
_7
return <UserInterface />
_7
}

Componentes individuais e sem lógica interna


_2
const componente = ({ isAdmin }) =>
_2
isAdmin ? <AdminInterface /> : <UserInterface />

Exportando tudo do arquivo index

Esse é um truque legal de importação/exportação, que não tem nada a ver com nosso código real, mas podemos obter resultados interessantes usando-o. É assim que costumamos import arquivos:


_3
import Cérebro from './Brain.png'
_3
import pasta from './BriefCase.png'
_3
import BuildingConstruction from './BuildingConstruction.png'

Agora usando um arquivo index dentro da pasta de imagens:

index.js
Copy

_17
import Brain from './Brain.png';
_17
import BriefCase from './BriefCase.png';
_17
import BuildingConstruction from './BuildingConstruction.png';
_17
import CowboyHat from './CowboyHat.png';
_17
import CrystalBall from './CrystalBall.png';
_17
import Desktop from './Desktop.png';
_17
...
_17
_17
export {
_17
Brain,
_17
BriefCase,
_17
BuildingConstruction,
_17
CowboyHat,
_17
CrystalBall,
_17
Desktop,
_17
...
_17
};

component.js
Copy

_1
import { Brain, BriefCase, BuildingConstruction } from 'public/Emojis'

Veja como transformamos 3 linhas em 1. Observe que não precisamos usar public/Emojis/index, JavaScript importa automaticamente o arquivo se o nome dele for index.

Transformando condicionais complicadas em variáveis

Veja um exemplo em que estamos verificando vários condicionais para verificar se podemos renderizar o painel de gerenciamento:


_2
if (user.roles.includes('ADMIN') || (user.age > 18 && user.reputation > 10))
_2
return renderManagementPannel()

Vamos refatorar o código transformando os complicados condicionais em variáveis:


_4
const isAdmin = user.roles.includes('ADMIN')
_4
const responsibleUser = user.age > 18 && user.reputation > 10
_4
_4
if (isAdmin || responsibleUser) return renderManagementPannel()

Veja como aumentamos as linhas em nosso código, mas nossa equipe trabalhando no aplicativo pode saber facilmente porque a função renderManagementPannel está sendo acionada.