Skip to main content

📘 Componentes e Estados no ReactJS

No capítulo anterior, vimos como uma aplicação React pode ser organizada em componentes e como as props permitem transmitir informações entre eles.

Agora avançaremos para outro conceito fundamental do React: o estado, também chamado de state.

Aplicações reais precisam reagir às ações do usuário. Por exemplo:

  • um usuário pode digitar seu nome em um campo;
  • uma mensagem de erro pode aparecer quando um valor é inválido;
  • um botão pode alterar seu texto depois de ser clicado;
  • informações exibidas na tela podem mudar durante a execução.

Para que um componente consiga armazenar informações que podem mudar ao longo do tempo, utilizamos o conceito de estado.


O que é um estado?​

Um estado (state) representa uma informação interna de um componente que pode mudar durante sua execução.

Imagine um botão que inicialmente apresenta:

Logar

Depois que o usuário clica nesse botão, queremos que seu texto seja alterado:

Você clicou!

O texto apresentado pelo botão precisa ser armazenado e posteriormente modificado. Esse é um exemplo de informação que pode ser controlada por um estado.

Estado e interface

Quando um estado utilizado pela interface é atualizado, o React realiza uma nova renderização do componente para que a interface represente o novo estado da aplicação.

Esse comportamento está relacionado à reatividade do React.


Props e State​

Embora props e state armazenem informações utilizadas pelos componentes, possuem finalidades diferentes.

PropsState
São recebidas de outro componentePertence ao próprio componente
Permitem transmitir informaçõesPermite armazenar informações internas
Não devem ser modificadas pelo componente receptorPode ser atualizado durante a execução
Favorecem a comunicação entre componentesPermite criar interfaces reativas

Props​

Nas props, uma informação é enviada de um componente para outro:

Componente Pai
│
│ props
▼
Componente Filho

Por exemplo:

<Label texto="Usuário:" />

O componente Label recebe "Usuário:" por meio da prop texto.

State​

No state, a informação é mantida pelo próprio componente:

┌─────────────────────┐
│ Componente │
│ │
│ state │
│ │ │
│ ▼ │
│ Interface (UI) │
└─────────────────────┘
Props x State

Props permitem que um componente receba informações.

State permite que o componente mantenha e atualize informações durante sua execução.


O Hook useState

Em Function Components, podemos criar estados utilizando o Hook useState.

import React, { useState } from 'react';

A estrutura básica é:

const [valor, setValor] = useState(valorInicial);

Podemos interpretar essa declaração assim:

const [valor, setValor] = useState(valorInicial);
│ │ │
│ │ └── valor inicial
│ │
│ └── função de atualização
│
└── valor atual do estado

Por exemplo:

const [nome, setNome] = useState("");

Nesse caso:

  • nome contém o valor atual;
  • setNome permite atualizar o estado;
  • "" é o valor inicial.

Para atualizar o estado, utilizamos:

setNome("Maria");
Importante

Uma variável de estado não deve ser alterada diretamente:

nome = "Maria";

Utilizamos sua função de atualização:

setNome("Maria");

Estrutura utilizada neste capítulo​

Continuaremos utilizando a aplicação de login:

index.html
│
▼
src/main.jsx
│
▼
src/App.jsx
│
▼
src/componentes/state/Login.jsx
│
├── Label.jsx
├── Labelln.jsx
├── InputText.jsx
├── InputPassword.jsx
└── Button.jsx

Nos próximos exemplos veremos como estados podem ser utilizados para armazenar valores, responder às interações do usuário e alterar dinamicamente a interface.

Estado em um Function Component

Vamos iniciar com o componente Label.jsx.

Ele recebe uma informação por meio de uma prop e utiliza essa informação para definir o valor inicial de um estado.

Código completo — Label.jsx​

import React, { useState } from 'react';

export default function Label({ texto }) {

const [valor, setValor] = useState(texto);

return (
<>
{valor}
</>
);
}

Entendendo o código​

Importação do useState​

import React, { useState } from 'react';

Além do React, importamos o Hook useState, utilizado para criar estados em componentes funcionais.


Recebendo uma prop​

export default function Label({ texto }) {

O componente recebe a prop texto por desestruturação.

Por exemplo:

<Label texto="Usuário:" />

Nesse caso:

texto = "Usuário:"

Criando o estado​

const [valor, setValor] = useState(texto);

Temos:

  • valor: valor atual do estado;
  • setValor: função que poderá atualizar o estado;
  • texto: valor utilizado para inicializar o estado.

O fluxo inicial é:

prop texto
│
▼
useState(texto)
│
▼
estado valor
│
▼
Interface

Renderizando o estado​

return (
<>
{valor}
</>
);

A expressão:

{valor}

faz com que o conteúdo armazenado no estado seja apresentado na interface.

info

Neste exemplo, a prop texto fornece o valor inicial do estado.

Depois de criado, valor passa a representar o estado interno do componente.


Reatividade​

Se posteriormente executarmos:

setValor("Novo valor");

o estado será atualizado.

Quando essa alteração influencia a interface, o React realiza uma nova renderização do componente, apresentando o novo valor.

valor atual
│
▼
setValor(...)
│
▼
novo estado
│
▼
nova renderização

Conceitos trabalhados​

Neste componente utilizamos:

  • Function Component;
  • props;
  • useState;
  • estado interno;
  • função de atualização;
  • JSX;
  • fragmentos;
  • reatividade.

Estado em Componentes de Classe

Até agora utilizamos Function Components com Hooks.

Entretanto, o React também possui a forma tradicional de construção de componentes utilizando classes.

Vamos observar essa abordagem no componente Labelln.jsx.

Código completo — Labelln.jsx​

import React from 'react';

export default class Labelln extends React.Component {

constructor(props) {
super(props);

this.state = {
valor: props.texto
};
}

render() {
return (
<>
{this.state.valor} <br/>
</>
);
}
}

Definindo um componente de classe​

export default class Labelln extends React.Component {

Diferentemente de:

function Label() {

agora estamos criando uma classe que herda de:

React.Component

Isso permite que o componente utilize recursos como props e state.


O construtor​

constructor(props) {
super(props);

this.state = {
valor: props.texto
};
}

O construtor é executado quando o componente é criado.

super(props)​

super(props);

Chama o construtor da classe pai React.Component.

this.state​

O estado é definido como um objeto:

this.state = {
valor: props.texto
};

Nesse exemplo, o estado valor é inicializado com a prop texto.


O método render()​

Componentes de classe utilizam o método:

render()

para definir o JSX que será apresentado.

render() {
return (
<>
{this.state.valor} <br/>
</>
);
}

Para acessar um valor do estado utilizamos:

this.state.valor

Function Component x Class Component​

Podemos comparar as duas formas.

Function Component​

const [valor, setValor] = useState(texto);

Class Component​

this.state = {
valor: props.texto
};

Para atualizar o estado em componentes de classe, utiliza-se setState().

Componentes de classe

Componentes de classe representam uma abordagem tradicional de construção de componentes React.

Eles ainda podem ser encontrados em projetos existentes, enquanto os exemplos modernos frequentemente utilizam Function Components e Hooks.


Fragmentos​

Observe também:

<>
{this.state.valor} <br/>
</>

Os fragmentos:

<>
</>

permitem agrupar elementos sem adicionar uma nova tag ao DOM.

Conceitos trabalhados​

  • Class Components;
  • React.Component;
  • constructor;
  • props;
  • super(props);
  • this.state;
  • setState();
  • render();
  • fragmentos.

Inputs Controlados

Agora utilizaremos estados para armazenar informações digitadas pelo usuário.

Criaremos o componente:

InputText.jsx

Além de armazenar o texto digitado, ele verificará se o campo está vazio.


Código completo — InputText.jsx​

import React, { useState } from 'react';

export default function InputText({ placeholder }) {

const [valor, setValor] = useState("");
const [erro, setErro] = useState(false);

function handleChange(e) {

const novoValor = e.target.value;

setValor(novoValor);

// verifica se está vazio
setErro(novoValor.trim() === "");
}

return (
<>
<input
type="text"
placeholder={placeholder}
value={valor}
onChange={handleChange}
/>

{erro && <p>⚠️ O campo não pode ficar vazio.</p>}
</>
);
}

Dois estados no mesmo componente

O componente possui dois estados:

const [valor, setValor] = useState("");
const [erro, setErro] = useState(false);

O primeiro armazena o conteúdo digitado:

valor

O segundo informa se existe uma situação de erro:

erro

Inicialmente:

valor = ""
erro = false
tip

Um componente pode possuir vários estados independentes.

Cada chamada a useState() cria um estado.


Eventos em React

Observe o atributo:

onChange={handleChange}

O evento onChange ocorre quando o conteúdo do campo é alterado.

Quando o usuário digita, a função:

handleChange

é executada.


O objeto de evento​

A função recebe um parâmetro:

function handleChange(e) {

Esse parâmetro representa o evento ocorrido.

Podemos obter o valor atual do input com:

e.target.value

No nosso exemplo:

const novoValor = e.target.value;

O fluxo é:

Usuário digita
│
▼
onChange
│
▼
handleChange(e)
│
▼
e.target.value
│
▼
novoValor

Atualizando o estado​

Depois de obter o conteúdo digitado:

setValor(novoValor);

atualizamos o estado valor.

Temos então:

INPUT
│
│ onChange
▼
handleChange
│
▼
setValor
│
▼
STATE

O que é um input controlado?

Observe:

<input
type="text"
placeholder={placeholder}
value={valor}
onChange={handleChange}
/>

O valor apresentado pelo input depende de:

value={valor}

Como valor é um estado React, esse elemento é denominado input controlado.

          ┌─────────────┐
│ STATE │
│ valor │
└──────┬──────┘
│
▼
<input>
│
│ onChange
▼
handleChange()
│
▼
setValor()
│
└────────► STATE
Input controlado

Em um input controlado, o valor apresentado pelo campo está sincronizado com um estado do React.

Isso facilita operações como validação e manipulação dos dados digitados.


Validação do campo

Também verificamos se o campo está vazio:

setErro(novoValor.trim() === "");

O método:

trim()

remove espaços existentes no início e no final da string.

Assim:

""       → vazio
" " → vazio após trim()
"Maria" → possui conteúdo

Se o campo estiver vazio:

setErro(true);

Caso contrário:

setErro(false);

Renderização condicional

Observe:

{erro && <p>⚠️ O campo não pode ficar vazio.</p>}

Esse recurso é denominado renderização condicional.

Podemos interpretar:

erro === true
│
▼
exibe a mensagem

Quando:

erro === false

o elemento não é apresentado.

A estrutura geral é:

condicao && <Componente />

O elemento JSX será renderizado quando a condição for verdadeira.


Conceitos trabalhados​

Neste componente utilizamos:

  • useState;
  • múltiplos estados;
  • onChange;
  • eventos;
  • e.target.value;
  • inputs controlados;
  • validação;
  • trim();
  • renderização condicional.

Controlando um Campo de Senha

Agora aplicaremos os mesmos conceitos a um campo de senha.

Criaremos:

InputPassword.jsx

Código completo — InputPassword.jsx​

import React, { useState } from 'react';

export default function InputPassword({ placeholder }) {

const [senha, setSenha] = useState("");

function handleChange(e) {
setSenha(e.target.value);
}

return (
<>
<input
type="password"
placeholder={placeholder}
value={senha}
onChange={handleChange}
/>

{(senha !== "") && <p>Senha digitada: {senha}</p>}
</>
);
}

Estado da senha​

Criamos o estado:

const [senha, setSenha] = useState("");

Temos:

senha

como valor atual e:

setSenha

como função de atualização.

O estado começa vazio:

senha = ""

Capturando a digitação​

A função:

function handleChange(e) {
setSenha(e.target.value);
}

é executada sempre que o usuário altera o campo.

O fluxo é:

Digitação
│
▼
onChange
│
▼
handleChange(e)
│
▼
e.target.value
│
▼
setSenha(...)
│
▼
state senha

type="password"

O elemento:

<input type="password" />

utiliza um tipo nativo do HTML destinado à entrada de senhas.

Os caracteres digitados são ocultados visualmente pelo navegador.

No nosso componente:

<input
type="password"
placeholder={placeholder}
value={senha}
onChange={handleChange}
/>

o campo também é controlado, pois seu valor depende do estado senha.


Renderização condicional

O exemplo utiliza:

{(senha !== "") && <p>Senha digitada: {senha}</p>}

A condição:

senha !== ""

verifica se existe algum conteúdo.

Se houver:

senha possui conteúdo
│
▼
condição verdadeira
│
▼
mensagem apresentada

Se o estado estiver vazio, a mensagem não será renderizada.

Exemplo didático

Exibir a senha digitada na página não é uma prática segura para aplicações reais.

Essa apresentação é utilizada neste exemplo exclusivamente para permitir a visualização da alteração do estado e da renderização condicional.


Conceitos trabalhados​

  • input controlado;
  • useState;
  • onChange;
  • e.target.value;
  • type="password";
  • estado;
  • renderização condicional.

Eventos e Alteração de Estado

Até agora utilizamos principalmente o evento:

onChange

Agora veremos outro evento muito comum:

onClick

Criaremos um botão cujo texto será alterado depois de um clique.


Código completo — Button.jsx​

import React, { useState } from 'react';

export default function Button({ valor }) {

const [texto, setTexto] = useState(valor);

function handleClick() {
setTexto("Você clicou!");
}

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

Prop como valor inicial

O componente recebe:

{ valor }

Por exemplo:

<Button valor="Logar" />

A prop é utilizada para inicializar o estado:

const [texto, setTexto] = useState(valor);

Inicialmente:

valor = "Logar"
│
▼
useState(valor)
│
▼
texto = "Logar"

Evento onClick

Observe:

<button onClick={handleClick}>

Quando o usuário clicar no botão, o React executará:

handleClick

A função é:

function handleClick() {
setTexto("Você clicou!");
}

Ela modifica o estado texto.


Fluxo completo​

Inicialmente:

texto = "Logar"

A interface apresenta:

┌───────────┐
│ Logar │
└───────────┘

O usuário clica:

       clique
│
▼
handleClick()
│
▼
setTexto("Você clicou!")
│
▼
novo estado

O React renderiza novamente o componente:

┌──────────────────┐
│ Você clicou! │
└──────────────────┘

Eventos em React

Os eventos React são utilizados como propriedades JSX.

Alguns exemplos:

onClick={funcao}
onChange={funcao}

Observe que os nomes seguem a convenção camelCase.

Por exemplo:

HTML             React

onclick → onClick
onchange → onChange

Props x State neste exemplo​

Este componente demonstra muito claramente a diferença entre os dois.

Prop​

valor

É recebida de fora:

<Button valor="Logar" />

State​

texto

É interno e pode mudar:

setTexto("Você clicou!");

Podemos representar:

valor (prop)
│
▼
useState(valor)
│
▼
texto (state)
│
│ clique
▼
setTexto(...)
│
▼
novo texto

Conceitos trabalhados​

  • useState;
  • estado dinâmico;
  • onClick;
  • manipulação de eventos;
  • props;
  • atualização de estado;
  • re-renderização;
  • reatividade.

Integrando os Componentes

Agora que cada componente possui uma responsabilidade específica, podemos integrá-los para construir a interface de login.

Utilizaremos:

Login.jsx
│
├── Labelln.jsx
├── Label.jsx
├── InputText.jsx
├── InputPassword.jsx
└── Button.jsx

Código completo — Login.jsx​

import Label from "./Label.jsx";
import Labelln from "./Labelln.jsx";
import Button from "./Button.jsx";
import InputText from "./InputText.jsx";
import InputPassword from "./InputPassword.jsx";

function Login() {

return (
<>
<Labelln texto="Login" />

<Label texto="Usuário: " />
<InputText placeholder="user..." />
<br />

<Label texto="Senha: " />
<InputPassword placeholder="password..." />
<br />

<Button valor="Logar" />
</>
);
}

export default Login;

Importando os componentes

No início do arquivo encontramos:

import Label from "./Label.jsx";
import Labelln from "./Labelln.jsx";
import Button from "./Button.jsx";
import InputText from "./InputText.jsx";
import InputPassword from "./InputPassword.jsx";

Cada funcionalidade está implementada em um componente separado.

Label
└── apresenta rótulos

Labelln
└── apresenta o título

InputText
└── controla o campo de usuário

InputPassword
└── controla o campo de senha

Button
└── controla o botão

Construindo a interface

O título é criado com:

<Labelln texto="Login" />

Depois temos o campo de usuário:

<Label texto="Usuário: " />
<InputText placeholder="user..." />

E o campo de senha:

<Label texto="Senha: " />
<InputPassword placeholder="password..." />

Finalmente:

<Button valor="Logar" />

Props personalizando componentes

Observe que os componentes são reutilizados com informações diferentes.

Por exemplo:

<Label texto="Usuário: " />

e:

<Label texto="Senha: " />

O componente é o mesmo:

Label

mas a prop:

texto

permite personalizar seu conteúdo.

O mesmo acontece com:

<InputText placeholder="user..." />

e:

<InputPassword placeholder="password..." />

Separação de responsabilidades

Um ponto importante é que Login.jsx não possui estado próprio neste exemplo.

Ele apenas organiza os componentes filhos.

                    Login
│
┌─────────────┼──────────────┐
│ │ │
▼ ▼ ▼
Label InputText Button
│
▼
Estado

Cada componente mantém a lógica necessária para sua própria responsabilidade.

Por exemplo:

InputText
├── valor
├── erro
└── handleChange

Enquanto:

Button
├── texto
└── handleClick

Componentização

A estrutura final demonstra uma das principais características do React: componentização.

Em vez de construir toda a interface em um único arquivo:

Login gigante

dividimos a aplicação:

Login
├── Label
├── InputText
├── Label
├── InputPassword
└── Button

Cada componente possui uma responsabilidade específica e pode ser reutilizado.

Reutilização

Um componente como:

<InputText />

não precisa ser exclusivo da tela de login.

Ele pode ser reutilizado em outros formulários, reduzindo a duplicação de código.


Visão geral do fluxo

Podemos representar a aplicação desenvolvida neste capítulo assim:

                    Login
│
┌─────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
Labels Inputs Button
│ │
▼ ▼
State State
│ │
▼ ▼
onChange onClick
│ │
▼ ▼
atualização atualização
│ │
└─────────┬──────────┘
▼
nova renderização

Consolidando os conceitos

Ao longo deste capítulo utilizamos diferentes recursos do React.

Props​

Permitem enviar informações para componentes:

<Label texto="Usuário:" />

State​

Permite armazenar informações que podem mudar:

const [valor, setValor] = useState("");

Eventos​

Permitem responder às ações do usuário:

onChange={handleChange}

e:

onClick={handleClick}

Inputs controlados​

Mantêm o valor do campo sincronizado com um estado:

<input
value={valor}
onChange={handleChange}
/>

Renderização condicional​

Permite apresentar elementos de acordo com uma condição:

{erro && <p>O campo não pode ficar vazio.</p>}

Componentização​

Permite dividir a interface em partes independentes e reutilizáveis:

Login
├── Label
├── InputText
├── InputPassword
└── Button

Resumo​

Neste capítulo avançamos de componentes que apenas recebem informações para componentes capazes de manter estado e reagir às ações do usuário.

O fluxo fundamental estudado pode ser resumido como:

Interação do usuário
│
▼
Evento
│
▼
Função manipuladora
│
▼
Atualização do estado
│
▼
React atualiza a interface

Essa relação entre estado, eventos e renderização será fundamental para a construção de interfaces cada vez mais interativas em ReactJS.

Conceitos-chave

Ao concluir este conteúdo, revise principalmente:

  • componentes;
  • props;
  • state;
  • useState;
  • setState();
  • eventos;
  • onChange;
  • onClick;
  • inputs controlados;
  • renderização condicional;
  • componentização;
  • separação de responsabilidades;
  • reutilização de componentes.

Exercício de Fixação

Neste capítulo foram apresentados diferentes componentes React envolvendo props, states, eventos, inputs controlados e renderização condicional.

Agora você deverá colocar em prática os conceitos estudados, implementando, executando e validando todos os códigos apresentados ao longo do conteúdo.

Atividade​

Utilizando o projeto ReactJS desenvolvido nas aulas anteriores, crie a estrutura:

src/
└── componentes/
└── state/
├── Login.jsx
├── Label.jsx
├── Labelln.jsx
├── InputText.jsx
├── InputPassword.jsx
└── Button.jsx

Implemente em cada arquivo os códigos apresentados neste capítulo.

Em seguida, ajuste o componente App.jsx para utilizar o componente Login.jsx.

Por exemplo:

import Login from "./componentes/state/Login.jsx";

function App() {
return (
<>
<Login />
</>
);
}

export default App;

Execute a aplicação e verifique o funcionamento de todos os componentes.


Testes e validações​

Durante a execução, realize os seguintes testes:

1. Componente Labelln​

Verifique se o componente:

<Labelln texto="Login" />

apresenta corretamente o texto:

Login

Observe que esse componente utiliza state em um componente de classe.


2. Componente Label​

Verifique se:

<Label texto="Usuário: " />

e:

<Label texto="Senha: " />

apresentam corretamente os valores recebidos pelas props.

Identifique no código onde a prop texto é utilizada para inicializar o estado do componente.


3. Componente InputText​

Digite diferentes valores no campo de usuário.

Verifique se o valor digitado é armazenado no estado:

valor

Apague todo o conteúdo do campo e observe se a mensagem:

⚠️ O campo não pode ficar vazio.

é apresentada.

Teste também inserir apenas espaços no campo e observe o comportamento da validação realizada com:

trim()

4. Componente InputPassword​

Digite uma senha no campo correspondente.

Verifique se:

  • os caracteres são ocultados pelo type="password";
  • o estado senha é atualizado durante a digitação;
  • a mensagem condicional é apresentada quando o estado possui conteúdo;
  • a mensagem deixa de ser apresentada quando o campo é esvaziado.
warning

A apresentação da senha digitada é utilizada exclusivamente para fins didáticos neste exercício.

Não utilize esse comportamento em aplicações reais.


5. Componente Button​

Observe inicialmente o texto:

Logar

Clique no botão.

Verifique se o texto é alterado para:

Você clicou!

Identifique no código a sequência responsável por essa alteração:

onClick
│
▼
handleClick()
│
▼
setTexto(...)
│
▼
novo estado
│
▼
nova renderização

6. Componente Login​

Por fim, verifique se o componente Login.jsx integra corretamente:

Login
│
├── Labelln
├── Label
├── InputText
├── Label
├── InputPassword
└── Button

Observe que o componente Login é responsável por organizar os demais componentes, enquanto cada componente filho mantém sua própria responsabilidade.


Validação final​

Ao concluir os testes, certifique-se de que a aplicação permite observar:

  • passagem de valores por props;
  • utilização de state;
  • utilização do Hook useState;
  • utilização de estado em Class Components;
  • atualização de estados;
  • eventos onChange e onClick;
  • captura de valores com e.target.value;
  • funcionamento de inputs controlados;
  • validação de campo vazio;
  • utilização de renderização condicional;
  • re-renderização após alterações de estado;
  • integração e reutilização dos componentes.
Desafio de observação

Durante os testes, procure identificar sempre o seguinte fluxo:

Interação do usuário
│
▼
Evento
│
▼
Função manipuladora
│
▼
Atualização do estado
│
▼
Atualização da interface

Compreender esse fluxo é mais importante do que apenas reproduzir os códigos.

Resultado esperado​

Ao final do exercício, a aplicação deverá apresentar uma interface de login funcional para fins didáticos, permitindo testar os diferentes comportamentos implementados nos componentes deste capítulo.

O objetivo do exercício não é acrescentar novas funcionalidades, mas executar, testar e compreender o funcionamento dos códigos apresentados.