Uso de variáveis e funções dentro de componentes

Uso de variáveis e funções dentro de componentes

Olá leitor, seja bem vindo a sexta lição da jornada de desenvolver ReactJS, e hoje começo te fazendo a seguinte pergunta: Você sabia que é possível adicionar uma lógica dentro dos nossos componentes?

Não? Pois saiba que não é só de return que um componente sobrevive 😉

E nós vamos descobrir isso agora!

Criando seu projeto de testes

Antes de começarmos, vamos criar um novo projeto em ReactJS chamado de variaveis-e-funcoes:

npx create-react-app variaveis-e-funcoes

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) 😁

Por fim, vamos criar uma nova página chamada de Home:

Home > index.jsx:

const Home = () => {
 return (
 <div>
 <h1>Home</h1>
 </div>
 );
}

export default Home;

E não se esqueça também de importar a Home dentro do App.js:

import Home from './pages/Home';

export default function App() {
 return(
 <>
 <Home />
 </>
 )
}

O que podemos fazer dentro de um componente?

No ReactJS, nós já vimos como funciona a declaração de um componente, não é verdade? Você ainda se lembra como se faz para declarar um?

const Componente = () => {
 return (
 <div>
 <h1>Componente</h1>
 </div>
 );
}

export default Componente;

Como você já sabe, um componente pode ser declarado dentro de uma constante que executa uma função anônima, que por sua vez, contém um return que retorna o JSX.

Só que... entre a primeira chave de abertura da função anônima e o comando return:

Pode existir uma infinidade de outros comandos, tais como declarações de variáveis e outras funções.

Dito isso, vamos aprender a criar nossas primeiras variáveis dentro dos componentes.

Criando variáveis dentro de componentes

De acordo com a nossa segunda lição da jornada Javascript, nós podemos declarar variáveis de 3 formas diferentes: const, var e let.

E dentro de um componente, as declarações não poderiam ser diferentes:

const Componente = () => {
 const nome = "Micilini";
 let rank = 10;
 var site = "https://www.micilini.com.br";
 return (
 <div>
 <h1>Componente</h1>
 </div>
 );
}

export default Componente;

Dentro de um componente podemos declarar quantas variáveis quisermos, não tendo um limite atribuido a elas.

Além disso, você pode declarar arrays, objetos ou variáveis que acionam uma determinada função.

Inserindo variáveis dentro do return

Se você quiser que o conteúdo de uma variável seja mostrada dentro do layout daquele componente, você vai precisar abrir as chaves ({}) dentro do comando return da seguinte forma:

const Componente = () => {
 const nome = "Micilini";
 let rank = 10;
 var site = "https://www.micilini.com.br";
 return (
 <div>
 <p>{nome} - {rank} - {site}</p>
 </div>
 );
}

export default Componente;

Quando abrimos as chaves ({}) dentro do return, estamos dizendo ao JSX que iremos implementar comandos Javascript, como declaração de variáveis, chamadas de funções, e pequenos comandos da linguagem.

Veja um outro exemplo onde estamos executando uma subtração dentro do return:

const Componente = () => {
 const nome = "Micilini";
 let rank = 10;
 var site = "https://www.micilini.com.br";
 return (
 <div>
 <p>{nome} - {rank} - {site}</p>
 <p>Calculo do Rank: {rank - 3}</p>
 </div>
 );
}

export default Componente;

Caso você deseje inserir um código Javascript gigantesco dentro do return, você também pode (apesar de não ser considerada uma boa prática):

const Componente = () => {
 const nome = "Micilini";
 let rank = 10;
 var site = "https://www.micilini.com.br";
 return (
 <div>
 <p>{nome} - {rank} - {site}</p>
 <p>Calculo do Rank: {rank - 3}</p>
 <p>
 {
 //Insira o código gigante dentro das chaves
 }
 </p>
 </div>
 );
}

export default Componente;

Cuidados ao se usar lógicas gigantescas dentro de um return

Apesar disso ser considerada uma má prática no JSX, o que poderia muito bem ser substituida por uma abstração de código usando uma função, ou seja, agrupar a lógica dentro de funções.

Você deve tomar alguns cuidados antes de inserir estruturas condicionais e outros comandos dentro do return.

Começando pela estrutura condicional if...else, você não pode usar diretamente uma declaração if, pois o JSX espera uma expressão. Nesse caso, é incorreto escrever:

<p>
 {
 if(rank > 5){
 return <p>Rank maior que 5</p>;
 } else {
 return <p>Rank menor que 5</p>;
 }
}
</p>

Em vez disso, você pode usar o operador condicional ternário (condition ? expr1 : expr2) ou envolver seu código if em chaves {} para criar um bloco de código:

 {rank > 5 ? <p>Rank maior que 5</p> : <p>Rank menor que 5</p>}

Observe que mesmo dentro do código Javascript, ainda existem tags do HTML sendo usadas. Isso é o JSX em ação.

Se preferir, você pode envolver o bloco de código if em chaves de modo a executar uma função anônima dentro de um bloco de código JavaScript no JSX:

const Componente = () => {
 const nome = "Micilini";
 let rank = 10;
 var site = "https://www.micilini.com.br";
 return (
 <div>
 <p>{nome} - {rank} - {site}</p>
 <p>Calculo do Rank: {rank - 3}</p>
 <p>
 {
 (() => {
 if (rank > 5) {
 return <p>Rank maior que 5</p>;
 } else {
 return <p>Rank menor que 5</p>;
 }
 })()
 }
 </p>
 </div>
 );
}

Observe que no código acima precisamos criar uma função anônima para poder utilizar a estrutura if...else de maneira convêncional.

Atenção: Se um dia você tentar inserir um código Javascript dentro do return do JSX e o compilador acusar um erro. Tente executar tal código dentro de uma função anônima (como fizemos no último bloco de código acima), isso pode  reparar o erro.

Executando mais de um return dentro de um componente

Em algumas situações, haverá momentos em que você vai precisar criar uma estrutura condiconal de modo a validar se o usuário está apto para entrar em uma tela administrativa ou não.

O exemplo do comando abaixo representa uma verificação de usuário válido, que checa se ele está logado ou não, e se não estiver ele é redirecionado a uma outra tela:

const Dashboard = () => {
	const isLoggedIn = false;
	
	if (!isLoggedIn) {//Verifica se o usuário está logado. Neste exemplo retorna false ele executa esse if
 history.push("/login");//Usamos a global history para redirecionar o usuário.
 return null;//Retornamos um JSX vazio
 }
	
 //Se este return form executado, isso significa que o usuário está logado e usufruir entrar na plataforma.
	return(
		<div>
			<h1>Seja bem vindo a plataforma</h1>
		</div>
	);
}


export default Dashboard;

Em aulas futuras veremos a melhor maneira de se fazer autenticação de usuários com ReactJS, mas no momento usamos o código acima para demonstrar a possibilidade do uso de uma condicional em um componente que possui diversos returns.

Veja um outro exemplo mais simples do uso de diversos returns:

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;

Observação: O comando return deve ser sempre um dos últimos comandos a serem inseridos dentro de um componente em ReactJS. Além disso, uma vez realizado o return, todos os outros returns existentes abaixo não serão exibidos, uma vez que o retorno já aconteceu.

Inserindo lógicas de maneira solta dentro de um componente

Apesar de não ser uma boa prática, você também pode executar algumas lógicas dentro de um componente, como um if...else, while, forEach, for e afins:

const Componente = () => {
 const nomes = ["Alice", "Bob", "Charlie"];

 const nomesJSX = [];

 nomes.forEach((nome, index) => {
 nomesJSX.push(<p key={index}>{nome}</p>);
 });

 return (
 <div>
 {nomesJSX}
 </div>
 );
}
const Componente = () => {
 const numeros = [1, 2, 3, 4, 5];

 const numerosJSX = [];

 let index = 0;

 while (index < numeros.length) {
 numerosJSX.push(<p key={index}>{numeros[index]}</p>);
 index++;
 }

 return (
 <div>
 {numerosJSX}
 </div>
 );
}
const Componente = () => {
 const numeros = [1, 2, 3, 4, 5];

 const numerosJSX = [];
 let index = 0;
 while (index < numeros.length) {
 numerosJSX.push(<p key={index}>{numeros[index]}</p>);
 index++;
 }

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

Note que o componente primeiro vai executar as lógicas inseridas, para depois retornar o JSX com os dados já processados.

Apesar desse estilo não estar em conformidade com as melhores práticas de desenvolvimento, o ideal é que façamos toda a lógica dentro de funções que podem existir em um componente.

E é o que veremos agora 🤩

Declarando funções dentro de componentes

Um componente em si, é uma função anônima (onde contém um return no final) que é executada por outro componente pai .

O Javascript nós permite criar funções aninhadas (funções dentro de outras funções), o que nós ajuda a criar subfunções dentro de um componente principal.

Para se declarar uma função dentro de um componente, é bem simples:

const ComponenteDois = () => {
 let nome = "Micilini";

 const mudaNome = () => {
 nome = "Micilini Roll";
 console.log(nome);
 }

 return(
 <>
 <h1>Nome: {nome}</h1>
 <p>{mudaNome()}</p>
 </>
 )
}

export default ComponenteDois;

No código acima, declaramos uma nova função chamada de mudaNome que altera o valor da variável nome que declaramos logo acima.

Apesar do componente não mudar o nome na tela do navegador assim que a função é executada, podemos ver o resultado no console.log indicando que o valor da variável nome realmente foi alterado:

Você pode declarar quantas funções desejar dentro de um componente:

const ComponenteDois = () => {
 let nome = "Micilini";

 const mudaNome = () => {
 nome = "Micilini Roll";
 mostraNome();
 }

 const mostraNome = () => {
 console.log(nome);
 }

 return(
 <>
 <h1>Nome: {nome}</h1>
 <p>{mudaNome()}</p>
 </>
 )
}

export default ComponenteDois;

Note que a função mudaNome chama outra função MostraNome no final de sua execução.

Apesar disso, talvez você esteja se perguntando: "Por que o console.log foi chamado duas vezes no exemplo acima?".

Por que o console.log do ReactJS aparece duplicado?

Para entender o principal motivo, gostaria que você desse uma olhada na ilustração abaixo:

A figura representa o ciclo de vida de uma aplicação em ReactJS.

Observe que essas funções consideradas "funções puras" podem ser pausadas durante o funcionamento da sua aplicação ou até mesmo reiniciadas pelo ReactJS. Isso significa que quando você renderiza o conteúdo JSX na tela do usuário, seu componente será montado inúmeras vezes de modo a reconstruir o virtualDOM com o DOM da sua aplicação.

Uma forma de conter essa duplicação é desativando o modo estrito (<StrictMode>) da sua aplicação dentro do index.js:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
 //<React.StrictMode>
 <App />
 //</React.StrictMode>
);

Mas tenha em mente que o modo estrito só deve ser desativado somente quando você vai fazer o deploy da sua aplicação (npm run build).

Uma vez que o modo estrito te ajuda a identificar problemas que podem estar ocorrendo dentro da sua aplicação. E é por esse motivo que o modo estrito, de forma intencional, acaba invocando duas vezes certas funções, tais como:

  • Construtor de componente de classe, método render e método shouldComponentUpdate (veremos o funcionamento deles na próxima lição),
  • Componentes do tipo classe, e funções de componentes que estão no corpo de um componente (veremos o funcionamento deles na próxima lição),
  • Funções passadas por useState, useMemo ou useReducer (veremos o funcionamento deles em lições futuras),

É importante ressaltar que Isso se aplica apenas ao modo de desenvolvimento. Os ciclos de vida não serão invocados duas vezes no modo de produção.

Todo componente precisa de um return?

Sim, todo componente ReactJS precisa retornar algo. Apesar do ReactJS não retornar nenhum tipo de erro quando fazemos isso:

const Componente = () => {
 const nome = "Micilini";
 let rank = 10;
 var site = "https://www.micilini.com.br";
}

export default Componente;

O ideal é que seu componente sempre retorne um JSX, porque se não, ele não precisaria ser chamado de componente, não é verdade? 🤣

Sendo assim, seria muito mais fácil transformar esse componente em um service de modo a usar uma extensão .js 🤣

Usando o onClick, onChange e onSubmit

No ReactJS principalmente quando trabalhamos com formulários, com frequência fazemos o uso dos atributos onClick, onChange e onSubmit que foram herdados do HTML.

Neste tópico vamos entender mais a fundo o funcionamento deles.

onClick

onClick é um evento no ReactJS que é acionado quando um elemento é clicado.

Ele é usado para adicionar interatividade aos elementos da interface do usuário, como botões, links e outros elementos clicáveis. 

Quando o evento onClick é acionado, a função associada a ele é executada, permitindo que você execute alguma lógica ou ação em resposta ao clique do usuário.

Por exemplo:

import React from 'react';

const MeuComponente = () => {
 const handleClick = () => {
 console.log('Botão clicado!');
 };

 return (
 <button onClick={handleClick}>
 Clique em mim
 </button>
 );
};

export default MeuComponente;

No exemplo acima, quando o usuário clica no botão (Clique em mim) o evento onClick é acionado, que por sua vez executa a função handleClick, que mostra uma mensagem no console.

Também é possível passar uma função como propriedade dentro do onClick, observe:

import React from 'react';

const Botao = ({ onClick }) => {
 return (
 <button onClick={onClick}>
 Clique em mim
 </button>
 );
};

const MeuComponente = () => {
 const handleClick = () => {
 console.log('Botão clicado!');
 };

 return (
 <Botao onClick={handleClick} />
 );
};

export default MeuComponente;

No exemplo acima estamos passando uma função de onClick para dentro de outro componente (Botão) para que esse acionamento aconteça por lá.

Também é possível fazer o uso de arrow functions dentro do onClick:

import React from 'react';

const MeuComponente = () => {
 return (
 <button onClick={() => console.log('Botão clicado!')}>
 Clique em mim
 </button>
 );
};

export default MeuComponente;

No caso do exemplo acima, estamos executando uma função anônima de uma única linha dentro do onClick.

onChange

onChange é um evento no ReactJS que é acionado quando o valor de um elemento de formulário é alterado pelo usuário. Ele é comumente usado em elementos de entrada, como <input>, <textarea>, e <select>.

Quando um usuário interage com um elemento de entrada, como digitando texto em um campo de texto ou selecionando uma opção de um menu suspenso, o evento onChange é disparado. 

Atenção

o atributo onChange costuma ser usado em componentes de classe, e como não vimos esse assunto ainda, talvez você não entenda o que está acontecendo nos exemplos abaixo. Sendo assim, você pode pular esse assunto e voltar depois 🙂

De qualquer forma, vou deixar o link da lição que fala sobre componentes de classe, em seguida volte aqui para ter um entendimento melhor do que está acontecendo.

Atualizando o estado de um componente de classe conforme o usuário digita em um campo de entrada:

import React, { Component } from 'react';

class MeuComponente extends Component {
 constructor(props) {
 super(props);
 this.state = {
 textoDigitado: ''
 };
 }

 handleChange = (event) => {
 this.setState({ textoDigitado: event.target.value });
 }

 render() {
 return (
 <input
 type="text"
 value={this.state.textoDigitado}
 onChange={this.handleChange}
 />
 );
 }
}

export default MeuComponente;

Fazendo uma busca em tempo real conforme o usuário digita em um campo de busca:

import React, { useState } from 'react';

const MeuComponente = () => {
 const [busca, setBusca] = useState('');

 const handleChange = (event) => {
 setBusca(event.target.value);
 // Aqui você pode realizar uma busca usando o valor de busca
 };

 return (
 <input
 type="text"
 value={busca}
 onChange={handleChange}
 placeholder="Pesquisar..."
 />
 );
};

export default MeuComponente;

Selecionando uma opção em um menu suspenso e atualizando o estado do componente conforme a seleção:

import React, { useState } from 'react';

const MeuComponente = () => {
 const [opcaoSelecionada, setOpcaoSelecionada] = useState('');

 const handleChange = (event) => {
 setOpcaoSelecionada(event.target.value);
 };

 return (
 <select value={opcaoSelecionada} onChange={handleChange}>
 <option value="opcao1">Opção 1</option>
 <option value="opcao2">Opção 2</option>
 <option value="opcao3">Opção 3</option>
 </select>
 );
};

export default MeuComponente;

Note que o evento onChange envia um parâmetro que podemos chamar de event, onde conseguimos acessar os valores usando event.target.value.

const handleChange = (event) => {
 console.log('O usuário mudou para: ' + event.target.value);
};

Na lição que fala sobre componentes de classe, você verá que os métodos setState são responsável por controlar o estado da aplicação em tempo real, o que vai de encontro com o uso do atributo onChange.

onSubmit

onSubmit é um evento em HTML e JavaScript que é acionado quando um formulário é enviado.

Em aplicações ReactJS, o evento onSubmit é frequentemente usado para lidar com o envio de formulários de forma controlada.

Quando um usuário envia um formulário clicando em um botão "Submit" ou pressionando Enter enquanto focado em um campo de entrada, o evento onSubmit é acionado.

import React from 'react';

const MeuComponente = () => {
 const handleSubmit = (event) => {
 event.preventDefault(); // Evita o comportamento padrão de envio do formulário
 const formData = new FormData(event.target);
 const dados = Object.fromEntries(formData.entries());
 console.log('Dados do formulário:', dados);
 };

 return (
 <form onSubmit={handleSubmit}>
 <input type="text" name="nome" placeholder="Nome" />
 <input type="email" name="email" placeholder="Email" />
 <button type="submit">Enviar</button>
 </form>
 );
};

export default MeuComponente;

No exemplo acima a função onSubmit é acionada, onde prontamente paramos o redirecionamento do formulário com o event.preventDefault(), note que usamos o FormData para pegar os dados do formulário, transformando-os em objetos para serem mostrados no console.

Em lições futuras aprenderemos um pouco mais sobre o uso real desses três atributos no ReactJS 😄

Arquivos da lição

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

Conclusão

Nesta lição você aprendeu a fazer o uso de variáveis e funções dentro dos seus componentes.

O que te ajudou a entender ainda mais o funcionamento deles e seus tipos de comportamentos.

Na próxima lição, vamos aprender um pouco mais sobre componentes funcionais e componentes de classe.