DEV Community

Vânia Gomes
Vânia Gomes

Posted on

Entendendo props e useState no React: Como Passar e Gerenciar Dados Entre Componentes

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:

  1. Vá até o chão perto da porta.
  2. Pegue todos os brinquedos que estão lá.
  3. Coloque os brinquedos na caixa de brinquedos.
  4. Vá até a cama.
  5. Arrume o travesseiro e o cobertor.
  6. Pegue os livros que estão no chão.
  7. 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:

  1. "Quando alguém deixar um brinquedo fora da caixa, coloque-o de volta."
  2. "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" },
  ],
};
Enter fullscreen mode Exit fullscreen mode

Agora, passamos author, publishedAt, e content para o componente Post usando props:

<Post
  author={post.author}
  publishedAt={post.publishedAt}
  content={post.content}
/>
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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} />
Enter fullscreen mode Exit fullscreen mode

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} />);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
  1. prevLikeCount é sempre o valor mais atualizado do estado.
  2. 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);
}
Enter fullscreen mode Exit fullscreen mode

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;
});

Enter fullscreen mode Exit fullscreen mode

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 (0)