Adicionando Interatividade

Algumas coisas na tela são atualizadas em resposta à entrada do usuário. Por exemplo, clicar em uma galeria de imagens muda a imagem ativa. No React, os dados que mudam ao longo do tempo são chamados de estado. Você pode adicionar estado a qualquer componente e atualizá-lo conforme necessário. Neste capítulo, você aprenderá como escrever componentes que tratam interações, atualizam seu estado e exibem diferentes saídas ao longo do tempo.

Respondendo a eventos

O React permite que você adicione manipuladores de eventos ao seu JSX. Manipuladores de eventos são suas próprias funções que serão acionadas em resposta a interações do usuário, como clicar, pairar, focar em entradas de formulário, e assim por diante.

Componentes embutidos como <button> só suportam eventos de navegador integrados, como onClick. No entanto, você também pode criar seus próprios componentes e dar aos props do manipulador de eventos nomes específicos de aplicação que desejar.

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert('Tocando!')}
      onUploadImage={() => alert('Carregando!')}
    />
  );
}

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Reproduzir Filme
      </Button>
      <Button onClick={onUploadImage}>
        Carregar Imagem
      </Button>
    </div>
  );
}

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

Ready to learn this topic?

Leia Respondendo a Eventos para aprender como adicionar manipuladores de eventos.

Read More

Estado: a memória de um componente

Os componentes frequentemente precisam mudar o que está na tela como resultado de uma interação. Digitar em um formulário deve atualizar o campo de entrada, clicar em “próximo” em um carrossel de imagens deve mudar qual imagem é exibida, clicar em “comprar” coloca um produto no carrinho de compras. Os componentes precisam “lembrar” de coisas: o valor atual de entrada, a imagem atual, o carrinho de compras. No React, esse tipo de memória específica do componente é chamado de estado.

Você pode adicionar estado a um componente com um Hook useState. Os Hooks são funções especiais que permitem que seus componentes utilizem recursos do React (o estado é um desses recursos). O Hook useState permite que você declare uma variável de estado. Ele recebe o estado inicial e retorna um par de valores: o estado atual e uma função de configuração de estado que permite atualizá-lo.

const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);

Aqui está como uma galeria de imagens usa e atualiza o estado ao clicar:

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);
  const hasNext = index < sculptureList.length - 1;

  function handleNextClick() {
    if (hasNext) {
      setIndex(index + 1);
    } else {
      setIndex(0);
    }
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Próximo
      </button>
      <h2>
        <i>{sculpture.name} </i>
        por {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} de {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Ocultar' : 'Mostrar'} detalhes
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </>
  );
}

Ready to learn this topic?

Leia Estado: A Memória de um Componente para aprender como lembrar um valor e atualizá-lo na interação.

Read More

Renderizar e confirmar

Antes que seus componentes sejam exibidos na tela, eles devem ser renderizados pelo React. Compreender as etapas desse processo ajudará você a pensar sobre como seu código é executado e explicar seu comportamento.

Imagine que seus componentes são cozinheiros na cozinha, montando pratos saborosos a partir dos ingredientes. Neste cenário, o React é o garçom que recebe pedidos dos clientes e leva seus pedidos. Este processo de solicitar e servir UI tem três etapas:

  1. Acionando uma renderização (entregando o pedido do cliente à cozinha)
  2. Renderizando o componente (preparando o pedido na cozinha)
  3. Confirmando no DOM (colocando o pedido na mesa)
  1. React como um servidor em um restaurante, buscando pedidos dos usuários e entregando-os à Cozinha do Componente.
    Acionar
  2. O Chef do Card oferece ao React um novo componente Card.
    Renderizar
  3. React entrega o Card ao usuário em sua mesa.
    Confirmar

Illustrated by Rachel Lee Nabors

Ready to learn this topic?

Leia Renderizar e Confirmar para aprender sobre o ciclo de vida de uma atualização de UI.

Read More

Estado como um instantâneo

Ao contrário das variáveis JavaScript regulares, o estado do React se comporta mais como um instantâneo. Configurá-lo não muda a variável de estado que você já possui, mas sim, aciona uma nova renderização. Isso pode ser surpreendente a princípio!

console.log(count); // 0
setCount(count + 1); // Solicitar uma nova renderização com 1
console.log(count); // Ainda 0!

Esse comportamento ajuda você a evitar erros sutis. Aqui está um pequeno aplicativo de chat. Tente adivinhar o que acontece se você pressionar “Enviar” primeiro e depois mudar o destinatário para Bob. Qual nome aparecerá no alert cinco segundos depois?

import { useState } from 'react';

export default function Form() {
  const [to, setTo] = useState('Alice');
  const [message, setMessage] = useState('Olá');

  function handleSubmit(e) {
    e.preventDefault();
    setTimeout(() => {
      alert(`Você disse ${message} para ${to}`);
    }, 5000);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Para:{' '}
        <select
          value={to}
          onChange={e => setTo(e.target.value)}>
          <option value="Alice">Alice</option>
          <option value="Bob">Bob</option>
        </select>
      </label>
      <textarea
        placeholder="Mensagem"
        value={message}
        onChange={e => setMessage(e.target.value)}
      />
      <button type="submit">Enviar</button>
    </form>
  );
}

Ready to learn this topic?

Leia Estado como um Instantâneo para aprender por que o estado aparece “fixo” e inalterado dentro dos manipuladores de eventos.

Read More

Enfileirando uma série de atualizações de estado

Este componente tem um bug: clicar em “+3” incrementa a pontuação apenas uma vez.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(score + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Pontuação: {score}</h1>
    </>
  )
}

Estado como um Instantâneo explica por que isso está acontecendo. Configurar o estado solicita uma nova renderização, mas não o muda no código que já está sendo executado. Assim, score continua sendo 0 logo após você chamar setScore(score + 1).

console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0

Você pode corrigir isso passando uma função de atualização ao definir o estado. Observe como substituir setScore(score + 1) por setScore(s => s + 1) corrige o botão “+3”. Isso permite que você enfileire múltiplas atualizações de estado.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(s => s + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Pontuação: {score}</h1>
    </>
  )
}

Ready to learn this topic?

Leia Enfileirando uma Série de Atualizações de Estado para aprender como enfileirar uma sequência de atualizações de estado.

Read More

Atualizando objetos no estado

O estado pode conter qualquer tipo de valor JavaScript, incluindo objetos. Mas você não deve mudar objetos e arrays que você mantém no estado do React diretamente. Em vez disso, quando você quiser atualizar um objeto ou array, precisa criar um novo (ou fazer uma cópia de um existente) e, em seguida, atualizar o estado para usar essa cópia.

Normalmente, você usará a sintaxe de expansão ... para copiar objetos e arrays que deseja alterar. Por exemplo, atualizar um objeto aninhado pode ser assim:

import { useState } from 'react';

export default function Form() {
  const [person, setPerson] = useState({
    name: 'Niki de Saint Phalle',
    artwork: {
      title: 'Blue Nana',
      city: 'Hamburg',
      image: 'https://i.imgur.com/Sd1AgUOm.jpg',
    }
  });

  function handleNameChange(e) {
    setPerson({
      ...person,
      name: e.target.value
    });
  }

  function handleTitleChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        title: e.target.value
      }
    });
  }

  function handleCityChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        city: e.target.value
      }
    });
  }

  function handleImageChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        image: e.target.value
      }
    });
  }

  return (
    <>
      <label>
        Nome:
        <input
          value={person.name}
          onChange={handleNameChange}
        />
      </label>
      <label>
        Título:
        <input
          value={person.artwork.title}
          onChange={handleTitleChange}
        />
      </label>
      <label>
        Cidade:
        <input
          value={person.artwork.city}
          onChange={handleCityChange}
        />
      </label>
      <label>
        Imagem:
        <input
          value={person.artwork.image}
          onChange={handleImageChange}
        />
      </label>
      <p>
        <i>{person.artwork.title}</i>
        {' por '}
        {person.name}
        <br />
        (localizada em {person.artwork.city})
      </p>
      <img
        src={person.artwork.image}
        alt={person.artwork.title}
      />
    </>
  );
}

Se copiar objetos em código se torna tedioso, você pode usar uma biblioteca como Immer para reduzir o código repetitivo:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Leia Atualizando Objetos no Estado para aprender como atualizar objetos corretamente.

Read More

Atualizando arrays no estado

Arrays são outro tipo de objetos JavaScript mutáveis que você pode armazenar no estado e devem ser tratados como somente leitura. Assim como com objetos, quando você quiser atualizar um array armazenado no estado, você precisa criar um novo (ou fazer uma cópia de um existente) e, em seguida, definir o estado para usar o novo array:

import { useState } from 'react';

const initialList = [
  { id: 0, title: 'Big Bellies', seen: false },
  { id: 1, title: 'Lunar Landscape', seen: false },
  { id: 2, title: 'Terracotta Army', seen: true },
];

export default function BucketList() {
  const [list, setList] = useState(
    initialList
  );

  function handleToggle(artworkId, nextSeen) {
    setList(list.map(artwork => {
      if (artwork.id === artworkId) {
        return { ...artwork, seen: nextSeen };
      } else {
        return artwork;
      }
    }));
  }

  return (
    <>
      <h1>Lista de Arte</h1>
      <h2>Minha lista de arte para ver:</h2>
      <ItemList
        artworks={list}
        onToggle={handleToggle} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

Se copiar arrays em código se torna tedioso, você pode usar uma biblioteca como Immer para reduzir o código repetitivo:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Leia Atualizando Arrays no Estado para aprender como atualizar arrays corretamente.

Read More

O que vem a seguir?

Vá para Respondendo a Eventos para começar a ler este capítulo página por página!

Ou, se você já está familiarizado com esses tópicos, por que não ler sobre Gerenciando Estado?