Renderização Condicional

Renderização Condicional

No universo do ReactJS, uma renderização condicional refere-se à técnica de renderizar componentes com base em certas condições.

Ou seja: "se essa afirmação for verdade, o componente retorna um layout, se não, um outro layout é mostrado no lugar",  e por aí vai...

Durante a 6° lição da nossa jornada, nós aprenderemos a retornar um layout diferente caso uma afirmação fosse verdadeira:

const ComponenteReturns = () => {
 const valor = 5;
	
 if (valor < 5) {
 return(
 <div>
 <p>Valor é menor que 5</p>
 </div>
 )
 }

 if (valor > 5) {
 return(
 <div>
 <p>Valor é maior que 5</p>
 </div>
 )
 }
 }

export default ComponenteReturns;

Tudo o que nós fizemos foi verificar se o valor armazenado dentro da variável valor fosse menor que 5, e em caso positivo, uma mensagem seria mostrada, em caso negativo, uma outra mensagem alternativa seria mostrada no lugar.

Como você pode ver, fizemos o uso de uma condicional (if) para validar as hipóteses e retornar o HTML correto.

Mas também poderíamos ter usado o else da seguinte forma:

const ComponenteReturns = () => {
 const valor = 5;
	
 if (valor < 5) {
 return(
 <div>
 <p>Valor é menor que 5</p>
 </div>
 )
 }else{
 return(
 <div>
 <p>Valor é maior que 5</p>
 </div>
 )
 }
 
}

export default ComponenteReturns;

Ambas às lógicas vistas acima, podem ser consideradas renderizações condicionais, nesta lição vamos aprender as diferentes formas de se utilizar condicionais.

Criando seu projeto de testes

Antes de começarmos, vamos criar um novo projeto em ReactJS dedicado a esta lição. No meu caso criei um novo projeto chamado de renderizacao-condicional:

npx create-react-app renderizacao-condicional

Após isso, não se esqueça de fazer aquela limpeza do código, e também aquela organizada nas pastas do src (organização por funcionalidade) 😁

If Statements

Seguindo a lógica que aprendemos na sexta lição, você pode usar instruções if  e else dentro dos seus componentes de modo a retornar layouts diferentes de acordo com uma determinada afirmação.

Exemplo de uma renderização em um componente funcional:

const ComponenteUm = () => {
 const valor = 10;
 if (valor < 10) {
 return <p>O valor é menor que 10!</p>;
 } else {
 return <p>O valor é maior que 10!</p>;
 }
};

export default ComponenteUm;

Exemplo de uma renderização em um componente de classe:

import React from 'react';

class ComponenteDois extends React.Component {
 render() {
 const valor = 10;
 if (valor < 10) {
 return <p>O valor é menor que 10!</p>;
 } else {
 return <p>O valor é maior que 10!</p>;
 }
 }
}

export default ComponenteDois;

Operador Ternário

Você pode usar o operador ternário (condição : true ? false) para retornar diferentes layouts dentro do seu componente.

Exemplo de uma renderização em um componente funcional:

const ComponenteTres = () => {
 const quantidadeDeContatos = 10;
 return (
 <div>
 <h1>Plataforma de Contatos</h1>
 {quantidadeDeContatos > 0 ? <p>Você possui contatos cadastrados!</p> : <p>Você não possui contatos cadastrados!</p>}
 </div>
 );
};

export default ComponenteTres;

Exemplo de renderização em um componente de classe:

import React from 'react';

class ComponenteQuatro extends React.Component {
 render() {
 const quantidadeDeContatos = 10;
 return (
 <div>
 <h1>Plataforma de Contatos</h1>
 {quantidadeDeContatos > 0 ? <p>Você possui contatos cadastrados!</p> : <p>Você não possui contatos cadastrados!</p>}
 </div>
 );
 }
}

export default ComponenteQuatro;

Com o operador ternário você também pode usar quebras de linhas para inserir um código JSX maior, observe:

const ComponenteCinco = () => {
 const status = 1;
 return(
 <div>
 {status ?//Colocamos um código Javascript de modo a verificar se o status é TRUE
 <div>
 <h2>O status é TRUE</h2>
 </div> ://Esses dois pontos representa uma espécie de ELSE do operador ternário
 <div>
 <h2>O status é FALSE</h2>
 </div>
 }
 <p>Eu vou aparecer, independentemente se o usuário estiver logado ou não...</p>
 </div>
 );
};
 
export default ComponenteCinco;

&& Operator

Você também pode utilizar o operador && para renderizar um componente condicionalmente com base em uma condição.

Exemplo de uma renderização em um componente funcional:

const ComponenteSeis = () => {
 const status = 1;
 return (
 <div>
 {status === 1 && (
 <div>
 <h1>Você está logado!</h1>
 <p>Pode fazer o que quiser dentro da plataforma =)</p>
 </div>
 )}
 <p>Eu vou aparecer, independentemente se o usuário estiver logado ou não...</p>
 </div>
 );
};
 
export default ComponenteSeis;

Observe que dentro das chaves ({}) ainda foi preciso encapsular (<div></div>) o conteúdo JSX que seria mostrado.

Caso houvesse apenas uma única tag HTML de retorno (como é demostrado no exemplo abaixo na tag <h1>), não há a necessidade de encapsulamento:

const ComponenteSeis = () => {
 const status = 1;
 return (
 <div>
 {status === 1 && (
 <h1>Você está logado!</h1>
 )}
 <p>Eu vou aparecer, independentemente se o usuário estiver logado ou não...</p>
 </div>
 );
};
 
export default ComponenteSeis;

Exemplo de uma renderização em um componente de classe:

import React from 'react';

class ComponenteSete extends React.Component {
 render() {
 const status = 1;
 return (
 <div>
 {status === 1 && (
 <div>
 <h1>Você está logado!</h1>
 <p>Pode fazer o que quiser dentro da plataforma =)</p>
 </div>
 )}
 <p>Eu vou aparecer, independentemente se o usuário estiver logado ou não...</p>
 </div>
 );
 }
}

export default ComponenteSete;

Renderização com Base em Listas

Haverá momentos em que você precisará renderizar diversos componentes dentro de um loop.

No caso do ReactJS nós costumamos percorrer uma lista usando a função forEach ou o map do Javascript.

Por exemplo, vamos supor que possuimos uma lista de nomes em um array, e que precisamos percorrer essa lista de modo a mostrar o nome dessas pessoas em uma tag <p>.

Exemplo de renderização de listas em um componente funcional:

const ListaDeNomes = () => {
 const nomes = ["João", "Maria", "Pedro", "Ana"];
 
 return (
 <div>
 <h1>Lista de Nomes</h1>
 <ul>
 {nomes.map((nome, index) => (
 <li key={index}>
 <p>Olá {nome}</p>
 </li>
 ))}
 </ul>
 </div>
 );
};
 
export default ListaDeNomes;

Aqui nós devemos nos atentar primeiramente ao atributo key que existe dentro do elemento <li>.

No mundo do ReactJS, sempre quando fazemos uma renderização com base em listas, você é obrigado a definir um atributo key para cada um dos elementos renderizados.

Ele se faz necessário, pois ajuda o ReactJS a identificar quais itens foram adicionados, removidos ou reordenados em uma lista durante as atualizações.

Abaixo listei algumas razões que fazem o atributo key seja essêncial na sua aplicação:

Identificação única dos elementos: Cada elemento em uma lista deve ter uma chave única para que o ReactJS possa distinguir entre eles. Isso garante que o ReactJS saiba qual elemento precisa ser atualizado, adicionado ou removido.

Melhor desempenho: O uso de chaves únicas ajuda o ReactJS a realizar operações de atualização de forma mais eficiente. O ReactJS pode fazer correspondência entre os elementos antigos e novos com base nas chaves, reduzindo o número de manipulações DOM necessárias durante as atualizações.

Evita erros de re-renderização desnecessária: Se você não fornecer chaves únicas para os elementos de uma lista, o React pode não ser capaz de identificar corretamente quais elementos foram alterados. Isso pode levar a erros de re-renderização desnecessária ou a problemas de estado incorreto na aplicação.

Ajuda na preservação do estado do componente: Se um elemento em uma lista possui um estado interno, o React precisa garantir que esse estado seja preservado durante as atualizações. O uso de chaves únicas ajuda o React a identificar os elementos corretos e preservar seus estados adequadamente.

Vamos analisar um outro exemplo onde as listas e keys se fazem necessárias:

const ListaDeCompras = () => {
 const listaDeCompras = {
 "João": ["Maçã", "Chiclete", "Leite"],
 "Paula": ["Feijão", "Arroz", "Carne"]
 };
 
 return (
 <div>
 <h1>Lista de Compras</h1>
 <ul>
 {Object.entries(listaDeCompras).map(([pessoa, itens]) => (
 <li key={pessoa}>
 <h3>{pessoa}</h3>
 <ul>
 {itens.map((item, index) => (
 <li key={index}>{item}</li>
 ))}
 </ul>
 </li>
 ))}
 </ul>
 </div>
 );
};
 
export default ListaDeCompras;

Observe que o atributo key esta sendo usado duas vezes durante a listagem.

Exemplo de renderização de listas em um componente de classe:

import React from 'react';

class ListaDeNomesClasse extends React.Component {
 render() {
 const nomes = ["João", "Maria", "Pedro", "Ana"];
 
 return (
 <div>
 <h1>Lista de Nomes</h1>
 <ul>
 {nomes.map((nome, index) => (
 <li key={index}>
 <p>{nome}</p>
 </li>
 ))}
 </ul>
 </div>
 );
 }
}

export default ListaDeNomesClasse;

Em resumo, o atributo key é essencial para garantir um comportamento previsível e eficiente ao renderizar listas dinâmicas em React. Sempre que você estiver renderizando elementos em uma lista, é uma boa prática fornecer chaves únicas para cada elemento.

Possíveis erros da não utilização do atributo key em listas em ReactJS

Considere que temos uma lista de nomes e estamos permitindo que o usuário adicione novos nomes à lista dinamicamente.

Se não fornecermos a chave (key) para cada elemento da lista, o ReactJS pode não ser capaz de identificar de forma eficiente quais elementos foram adicionados, removidos ou modificados.

Isso pode resultar em uma renderização incorreta ou ineficiente da lista.

Para testar isso, vamos criar um novo componente chamado de NameList.

NameList > index.jsx:

import React, { Component } from 'react';

class NameList extends Component {
 constructor(props) {
 super(props);
 this.state = {
 names: ['Alice', 'Bob', 'Charlie'],
 newName: ''
 };
 this.handleChange = this.handleChange.bind(this);
 this.handleSubmit = this.handleSubmit.bind(this);
 }

 handleChange(event) {
 this.setState({ newName: event.target.value });
 }

 handleSubmit(event) {
 event.preventDefault();
 const { names, newName } = this.state;
 this.setState({
 names: [...names, newName],
 newName: ''
 });
 }

 render() {
 const { names, newName } = this.state;

 const nameListItems = names.map(name => {
 return <li>{name}</li>; // Esquecemos de adicionar o atributo key aqui
 });

 return (
 <div>
 <h2>Lista de Nomes</h2>
 <ul>
 {nameListItems}
 </ul>
 <form onSubmit={this.handleSubmit}>
 <input type="text" value={newName} onChange={this.handleChange} />
 <button type="submit">Adicionar Nome</button>
 </form>
 </div>
 );
 }
}

export default NameList;

Neste exemplo, os novos nomes adicionados à lista não terão uma chave única associada a eles.

Como resultado, o ReactJS pode não conseguir distinguir adequadamente entre os diferentes elementos da lista.

Isso pode levar a problemas como re-renderizações desnecessárias de elementos, comportamento inesperado ao atualizar a lista e até mesmo erros de renderização.

Duplicação de elementos ao adicionar ou editar: Quando um novo elemento é adicionado à lista sem uma chave única associada a ele, o ReactJS pode interpretar incorretamente a lista. Isso pode resultar na duplicação de elementos existentes, especialmente durante a atualização do estado do componente. Por exemplo, ao adicionar um novo nome à lista, o novo nome pode ser exibido várias vezes em vez de apenas uma.

Comportamento inesperado ao reordenar ou excluir elementos: Sem chaves únicas, o ReactJS pode ter dificuldade em identificar corretamente os elementos que foram reordenados ou excluídos da lista. Isso pode levar a comportamentos inesperados, como elementos reordenados que não refletem corretamente a ordem no estado interno do componente, ou elementos excluídos que permanecem visíveis na interface do usuário.

Desempenho reduzido durante a renderização: Sem chaves únicas, o Reactjs pode ser menos eficiente na renderização de listas dinâmicas. Isso ocorre porque o ReactJS precisa fazer mais trabalho para reconciliar o estado interno do componente com a representação visual da lista na interface do usuário. Como resultado, a renderização pode ser mais lenta e menos responsiva, especialmente em listas grandes ou em dispositivos com recursos limitados.

Portanto... nunca se esqueça de usar o atributo key!

Renderização condicional em tempo real com componentes de classe

Na lição passada, nós conhecemos os componentes de classe, e lá foi dito que esses componentes possuem uma habilidade bem específica relacionada com os controles dos estados dos componentes.

Infelizmente, naquele momento não chegamos a ver isso em ação, pois ainda não era a hora 🥲

Pois bem, a hora chegou e acredito que você já está aguniado com esse suspense todo, não? (Tudo bem se você responder NÃO 😂)

Vamos começar criando um novo componente chamado Botao, onde a ideia principal é mudar as mensagens que aparecem assim que o usuário clica em um <button>.

Botao > index.jsx:

import React, { Component } from 'react';

class Botoes extends Component{

 constructor(props){
 super(props)
 this.state = {
 status: false//Setamos inicialmente como FALSE para simular que o usuário esta sempre deslogado quando entramos nessa tela pela primeira vez
 }
 
 this.entrar = this.entrar.bind(this);
 this.sair = this.sair.bind(this);
 }

 entrar(){
 this.setState({status: true})
 }

 sair(){
 this.setState({status: false})
 }

 render(){
 return(
 <div>
 {this.state.status ?
 <div>
 <h2>Você está logado no sistema</h2>
 <button onClick={this.sair}>Sair do Sistema</button>
 </div>
 :
 <div>
 <h2>Você está deslogado do sistema</h2>
 <button onClick={this.entrar}>Entrar no Sistema</button>
 </div>
 } 
 </div>
 );
 }
}

export default Botoes;

Note que estamos usando os atributos onClick para chamar métodos da classe, que por sua vez fazem o uso do setState para mudar os estados.

Por baixo dos panos, o ReactJS utiliza o método setState para atualizar o estado interno de um componente. Quando o estado é atualizado, o ReactJS detecta automaticamente essas mudanças e re-renderiza os componentes afetados, refletindo as atualizações na interface do usuário sem a necessidade de intervenção direta do desenvolvedor.

E como a variável status está conectada no JSX, o ReactJS já realiza essas alterações automaticamente.

Dê uma olhada no resultado final:

O mais interessante é que isso tudo aconteceu sem a necessidade do usuário atualizar a tela do navegador 🤩

Partiu pra outro exemplo?

Vamos criar agora um novo componente chamado de FraseAleatoria, cujo o objetivo é mudar a frase que aparece na tela, sempre quando um usuário clica no botão nova frase:

FraseAleatoria > index.jsx:

import React from 'react';

class FraseAleatoria extends React.Component {
 constructor(props) {
 super(props);
 this.state = {
 frases: [
 "O sucesso é ir de fracasso em fracasso sem perder entusiasmo.",
 "Se você quer algo que nunca teve, precisa fazer algo que nunca fez.",
 "A persistência é o caminho do êxito.",
 "Não espere por circunstâncias ideais, tome decisões e faça acontecer."
 ],
 fraseAtual: "Clique no botão para ver uma frase!"
 };
 }

 handleClick = () => {
 const { frases } = this.state;
 const novaFrase = frases[Math.floor(Math.random() * frases.length)];
 this.setState({ fraseAtual: novaFrase });
 };

 render() {
 const { fraseAtual } = this.state;
 return (
 <div>
 <h1>Frase Aleatória</h1>
 <p>{fraseAtual}</p>
 <button onClick={this.handleClick}>Nova Frase</button>
 </div>
 );
 }
}

export default FraseAleatoria;

Veja como ficou o resultado final:

Se fossemos fazer o uso de bibliotecas como jQuery ou alguma outra, nós deveríamos criar duas telas diferentes, ou quem sabe definir escutas (listners) para que a atualização aconteça em tempo real sem a necessidade de atualizar a página.

No caso do ReactJS isso acontece de maneira um pouco mais fácil usando menos código.

Para finalizar vamos fazer o nosso último exemplo deste tópico 🤪, cujo o objetivo é mudar a cor do texto toda vez que o usuário clica no botão. Para isso vamos criar um novo componente chamado de MudarCorTexto.

MudarCorTexto > index.jsx:

import React from 'react';

class MudarCorTexto extends React.Component {
 constructor(props) {
 super(props);
 this.state = {
 cor: 'black'
 };
 }

 handleClick = () => {
 const cores = ['red', 'blue', 'green', 'orange', 'purple'];
 const novaCor = cores[Math.floor(Math.random() * cores.length)];
 this.setState({ cor: novaCor });
 };

 render() {
 const { cor } = this.state;
 return (
 <div>
 <h1 style={{ color: cor }}>Cor do Texto</h1>
 <p style={{ color: cor }}>Este texto está na cor {cor}.</p>
 <button onClick={this.handleClick}>Mudar Cor</button>
 </div>
 );
 }
}

export default MudarCorTexto;

Note que estamos alterando o valor da variável cor, que por sua vez esta conectada ao atributo cor do style dos elementos HTML. Isso é possível pois o style nada mais é do que um objeto do Javascript, o que nos possibilita passar variáveis para dentro dele.

E como em um componente de classe as coisas estão conectadas e interligadas... a cor do seu botão mudará automaticamente 😁

Veja como ficou o resultado final:

Trabalhando com Animações em componentes de classe

No ReactJS também podemos animar elementos HTML sem a necessidade de usar bibliotecas de terceiros.

Por exemplo, supondo que queremos animar um determinado título assim que o usuário clica no botão, nós podemos fazer isso da seguinte forma:

Primeiro devemos criar o nosso componente principal, no meu caso, chamei ele de Animacao.

Animacao > index.jsx:

import React, { Component } from 'react';
import './animacao.css';

class Animacao extends Component {
 constructor(props) {
 super(props);
 this.state = {
 animateTitle: false
 };
 }

 handleClick = () => {
 this.setState({ animateTitle: true });
 setTimeout(() => {
 this.setState({ animateTitle: false });
 }, 1000); // Define a duração da animação em milissegundos
 };

 render() {
 const { animateTitle } = this.state;

 return (
 <div>
 <button onClick={this.handleClick}>Clique aqui</button>
 <h1 className={animateTitle ? 'animated' : ''}>Título Animado</h1>
 </div>
 );
 }
}

export default Animacao;

Animacao > animacao.css:

.animated {
 animation: animateTitle 1s forwards;
}
 
@keyframes animateTitle {
 from {
 padding-left: 0;
 }
 to {
 padding-left: 50px; /* Valor desejado de padding-left */
 }
}

Como podemos ver, estamos alterando o padding-left do elemento <h1>, por meio do @keyframes do CSS, o que já é o suficiente para mover o nosso elemento da esquerda para a direita.

Isso tudo aconteceu quando adicionamos a className animated no elemento <h1> quando a variável animateTitle se torna true.

Veja como ficou o resultado final:

Incrível não? 🙂

Trabalhando com listas - Parte 2

Para você ficar ainda mais fera em listas usando componentes em ReactJS, vamos montar uma quinta aplicação onde iremos listar os usuários usando um segundo componente.

Para isso vamos criar um novo componente chamado de ListaUsuarios.

ListaUsuarios > index.jsx:

import React, { Component } from 'react';
import Feed from '../Feed';

class ListaUsuarios extends Component{

 constructor(props){
 super(props)
 this.state = {
 feed:[
 {id: 1, username: 'Micilini', curtidas: 10, comentarios: 2},
 {id: 2, username: 'Minar', curtidas: 120, comentarios: 21},
 {id: 3, username: 'Roger', curtidas: 130, comentarios: 22},
 {id: 4, username: 'Gauzo', curtidas: 0, comentarios: 1},
 ]
 }
 }

 render(){
 return(
 <div>
 {this.state.feed.map((item) => {
 return(
 <Feed key={item.id} username={item.username} curtidas={item.curtidas} comentarios={item.comentarios} />
 );
 })}
 </div>
 );
 }
}

export default ListaUsuarios;

Em seguida precisamos criar o nosso componente filho, que será o Feed.

Feed > index.jsx:

import React, { Component } from 'react';

class Feed extends Component{
 render(){
 return(
 <div key={this.props.key}>
 <h3><strong>Nome:</strong> {this.props.username}</h3>
 <a><strong>{this.props.curtidas <= 1 ? 'Curtida' : 'Curtidas'}:</strong> {this.props.curtidas} | <strong>{this.props.comentarios <= 1 ? 'Comentário' : 'Comentários'}:</strong> {this.props.comentarios}</a>
 </div>
 );
 }
}

export default Feed;

No projeto acima, usamos o comando map para interar com o objeto que contém os usuários, de modo a passar as informações ao componente Feed para elas possam ser tratadas por lá.

Além disso, estamos usando a renderização condicional para verificar se usamos o termo curtida ou curtidas.

Veja como ficou o resultado final:

Arquivos da lição

Os arquivos dessa lição podem ser encontrados neste repositório do GitHub.

Conclusão

Nesta lição você aprendeu um pouco mais sobre como executar renderizações condicionais e ainda criou 5 projetos diferentes usando componentes de classe.

Na próxima lição, enfim aprenderemos a fazer o uso dos componentes de estado e abandonar de vez os componentes de classe 🤩