Para entender o uso de props e useState no React, vamos dar um passo atrás e conhecer dois paradigmas de programação: a programação imperativa e a programação declarativa. Compreender esses conceitos ajuda a entender como o React funciona.
Na programação imperativa, o foco está em "como fazer". O programador escreve um código que orienta
o computador, passo a passo
, em cada detalhe necessário para resolver um problema.
Imagine que você está ensinando seu amigo robô a limpar o quarto. Na abordagem imperativa, você vai dar uma lista completa e detalhada de tudo o que ele deve fazer, sem pular nenhuma etapa.
Exemplo de Limpeza Imperativa:
- Vá até o chão perto da porta.
- Pegue todos os brinquedos que estão lá.
- Coloque os brinquedos na caixa de brinquedos.
- Vá até a cama.
- Arrume o travesseiro e o cobertor.
- Pegue os livros que estão no chão.
- Coloque os livros na estante.
Aqui, você explica exatamente como fazer cada coisa, do começo ao fim, garantindo que nenhuma etapa seja esquecida.
Na programação declarativa, em vez de explicar tudo o que deve ser feito, você dá instruções para que as coisas aconteçam quando algo específico ocorrer. É como deixar uma regra pronta e deixar o robô agir quando necessário, sem precisar de você toda hora para dizer o que fazer.
Como Funciona?
Imagine que você fala para o seu amigo robô assim:
- "Quando alguém deixar um brinquedo fora da caixa, coloque-o de volta."
- "Quando a cama estiver desarrumada, arrume-a."
Essas são instruções declarativas! O robô entende que, se alguém deixar um brinquedo fora da caixa, ele deve guardar o brinquedo. E se a cama estiver bagunçada, ele arruma sozinho. Você não precisa explicar o passo a passo; o robô vai perceber quando uma dessas coisas acontecer e vai resolver.
No React, a programação declarativa facilita a criação de interfaces de maneira mais simples e organizada. Com ela, você usa props e useState para definir o que a interface deve mostrar em resposta a mudanças, sem precisar de um passo a passo detalhado.
Como props e useState funcionam na Programação Declarativa do React
Na abordagem declarativa, o React permite que você descreva o que deseja que apareça na interface. As props funcionam como "informações" passadas de um componente para outro, definindo o que deve ser exibido ou utilizado em cada componente. O useState, por sua vez, permite que o componente tenha seu próprio "estado" e reaja automaticamente a mudanças nesse estado, atualizando a interface conforme necessário.
No React, props e useState são fundamentais para gerenciar dados e criar interatividade. Vamos explorar como essas duas funcionalidades funcionam e como usá-las para passar dados entre componentes e armazenar valores dinâmicos no estado.
Trabalhando com props: Como Passar Dados Entre Componentes
As props (abreviação de "propriedades") permitem que dados sejam passados de um componente pai
para um componente filho
. Isso possibilita a criação de uma estrutura onde componentes podem receber informações específicas e renderizá-las de acordo.
Exemplo de Passagem de props
Suponha que temos um objeto post
com informações sobre o autor e conteúdo:
const post = {
author: {
name: "Dr. Ana Maria",
role: "Psicóloga",
avatarUrl: "/images/ana-avatar.png",
},
publishedAt: "2024-11-01",
content: [
{ type: "paragraph", content: "Lembre-se de praticar a gratidão diariamente." },
{ type: "link", content: "https://bemestar.com.br" },
],
};
Agora, passamos author, publishedAt, e content para o componente Post
usando props:
<Post
author={post.author}
publishedAt={post.publishedAt}
content={post.content}
/>
Acessando props
no Componente Filho
function Post(props) {
return (
<div>
<img src={props.author.avatarUrl} alt={`${props.author.name} avatar`} />
<h3>{props.author.name}</h3>
<p>{props.author.role}</p>
<span>{props.publishedAt}</span>
</div>
);
}
Desestruturação de props: Simplificando o Código
Usar desestruturação para acessar props diretamente no parâmetro da função deixa o código mais legível:
function Post({ author: { name, role, avatarUrl }, publishedAt }) {
return (
<div>
<img src={avatarUrl} alt={`${name} avatar`} />
<h3>{name}</h3>
<p>{role}</p>
<span>{publishedAt}</span>
</div>
);
}
Com a desestruturação, cada propriedade é acessada sem a necessidade de prefixar props, deixando o código mais claro.
Gerenciamento de Estado com useState: Controlando Dados Dinâmicos no React
O React oferece o useState para armazenar valores
que mudam ao longo do tempo, como uma lista de comentários que cresce com a interação do usuário. Quando o estado muda, o React re-renderiza o componente automaticamente para refletir as novas informações.
Atualizando o Estado com Imutabilidade
Usar o operador de espalhamento (...) é uma prática importante no React para preservar a imutabilidade:
Exemplo de Uso do useState
setComments([...comments, newCommentText]);
Isso cria uma nova cópia de comments com o novo comentário adicionado, mantendo o array original inalterado. A imutabilidade é essencial para garantir que o React detecte mudanças no estado e re-renderize o componente adequadamente.
Imagine que temos um componente Blog _ que exibe um _post e permite adicionar comentários. Usamos useState para controlar o array de comentários e o texto do novo comentário:
import React, { useState } from "react";
function Blog({ author, content }) {
const [comments, setComments] = useState(["Esse post é muito bacana!"]);
const [newCommentText, setNewCommentText] = useState("");
const handleNewCommentChange = (event) => {
setNewCommentText(event.target.value);
};
const handleAddComment = () => {
setComments([...comments, newCommentText]);
setNewCommentText(""); // Limpa a textarea após adicionar o comentário
};
return (
<div>
<h3>Post de {author.name}</h3>
<p>{content}</p>
<h4>Comentários:</h4>
<ul>
{comments.map((comment, index) => (
<li key={index}>{comment}</li>
))}
</ul>
<textarea
value={newCommentText}
onChange={handleNewCommentChange}
placeholder="Escreva seu comentário aqui..."
/>
<button type="button" onClick={handleAddComment}>
Adicionar Comentário
</button>
</div>
);
}
Comunicação entre Componentes com props e useState
Para permitir que o componente pai (como o Blog) controle o comportamento de componentes filhos, podemos passar funções como props:
<ChildComponent onDeleteComment={deleteCommentHandler} />
Essa estrutura permite que o componente filho invoque a função deleteCommentHandler, gerenciada pelo componente pai.
**Boas Práticas no React: Entendendo a Key
O Papel da Key
A propriedade key
é fundamental para o React identificar de forma eficiente quais itens em uma lista foram:
- Adicionados
- Removidos
- Reordenados.
Essa identificação evita que o React recrie toda a lista durante a renderização, atualizando apenas os elementos necessários e otimizando a performance.
O Problema
Ao inspecionar a aplicação no console, podem surgir erros relacionados à ausência de uma propriedade key
nos elementos de uma lista renderizada. Esse erro acontece porque o React precisa de uma key
única para diferenciar cada elemento da lista.
**Como Resolver o Erro
Adicione uma key
única a cada elemento da lista:
- A
key
deve ser exclusiva para cada item da lista. - Use uma propriedade consistente, como um
id
.
Exemplo:
posts.map((post) => <Post key={post.id} {...post} />);
Evite usar o índice do array como key
Embora o índice funcione em casos simples, ele pode causar problemas ao adicionar, remover ou reordenar itens na lista, levando a renderizações desnecessárias.
Por Que Não Usar o Índice do Array?
O índice do array não reflete mudanças na posição dos itens.
Exemplo:
Se o item 5
for movido da posição 2
para 4
, o React pode interpretar erroneamente a mudança, recriando elementos desnecessariamente. Isso prejudica a performance.
E se Não Houver um id
?
Em casos onde não exista um id
único, utilize outra propriedade que seja:
- Exclusiva
- Consistente
- E representativa do item como o conteúdo do elemento.
Características de uma Key Ideal
-
Única: Cada item da lista deve ter sua própria
key
. -
Estável: O valor da
key
não deve mudar entre renderizações. -
Representativa: Use informações que identifiquem claramente cada item, como um
id
ou conteúdo exclusivo.
Evite:
- Usar índices de arrays.
- Gerar valores aleatórios no momento da renderização.
Entendendo Atualizações de Estado no React
O Problema
Ao implementar uma funcionalidade de "likes" no React, você pode perceber que, mesmo executando o incremento duas vezes seguidas, o número de likes aumenta apenas de 1 em 1.
Isso ocorre porque o React não atualiza imediatamente o estado na memória. Ele agende uma nova renderização, e qualquer código que dependa do valor atualizado do estado será executado com o valor anterior, criando uma inconsistência.
Por Que Isso Acontece?
1. Atualização de Estado no React
Quando você chama setState
(como setLikes
):
- O React não altera imediatamente o valor do estado.
- Em vez disso, ele agende uma atualização e recalcula o componente em uma nova renderização.
2. Contexto de Execução
- O React cria um novo "contexto" para cada renderização.
- Se você usar
setLikes(likes + 1)
duas vezes seguidas, ambas as chamadas usarão o valor do estado atual no contexto antigo, resultando em apenas um incremento.
3. Closure no React
- As funções usadas para atualizar o estado fecham o contexto em que foram criadas.
- Mesmo após a atualização do estado, as execuções subsequentes ainda utilizam o valor anterior.
Soluções para o Problema
1. Criar uma Variável Temporária
Uma abordagem simples é armazenar o novo valor em uma variável antes de atualizar o estado.
Exemplo:
const newLikeCount = likeCount + 1;
setLikeCount(newLikeCount);
console.log(newLikeCount); // Valor atualizado
Aqui, a variável newLikeCount reflete o valor atualizado, enquanto o estado ainda não foi atualizado na memória.
2. Usar o Callback do setState
O React permite passar uma função para setState ao invés de um valor direto. Essa função recebe o valor mais recente do estado como argumento, garantindo que múltiplas atualizações usem o valor correto.
Exemplo:
setLikeCount((prevLikeCount) => prevLikeCount + 1);
-
prevLikeCount
é sempre o valor mais atualizado do estado. - Isso resolve problemas de dependência entre atualizações consecutivas.
Por Que Usar o Callback é melhor?
Ele garante que você está sempre utilizando o valor mais recente do estado.
É a abordagem recomendada quando a nova atualização depende do valor anterior do estado, como somar ou subtrair valores.
Exemplo Prático: Incrementar de 2 em 2
Se você quiser incrementar o número de likes de 2 em 2, pode usar o callback para garantir o funcionamento correto:
function handleLike() {
setLikeCount((prevLikeCount) => prevLikeCount + 1);
setLikeCount((prevLikeCount) => prevLikeCount + 1);
}
Com o uso do callback, cada atualização utiliza o valor mais recente, resultando em incrementos precisos.
Dica Final
Sempre que a nova atualização do estado depender do valor anterior, use o callback. Essa prática evita bugs sutis e torna seu código mais robusto e previsível.
Padrão recomendado:
setState((prevState) => {
// Atualização segura e previsível
return prevState + novoValor;
});
Com essas soluções, você pode lidar de forma eficiente com as atualizações de estado no React e criar funcionalidades mais estáveis e performáticas.
Conclusão
Ao entender props e useState, você pode criar componentes React que são dinâmicos, reutilizáveis e fáceis de manter. Props permitem passar dados entre componentes e useState permite gerenciar valores que mudam ao longo do tempo, tudo de maneira que o React re-renderize apenas o necessário.
Top comments (1)
Excelente artigo🚀🚀