Adicionando Interatividade

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

Respondendo a eventos

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

Componentes built-in, como <button> suportam apenas eventos built-in do navegador, como onClick. No entanto, você também pode criar seus próprios componentes e dar a suas props de event handler quaisquer nomes específicos da sua aplicação.

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

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Play Movie
      </Button>
      <Button onClick={onUploadImage}>
        Upload Image
      </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 event handlers.

Read More

State: a component’s memory

Componentes frequentemente precisam mudar o que está na tela como resultado de uma interação. Digitar no 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” das coisas: o valor atual da entrada, a imagem atual, o carrinho de compras. Em React, esse tipo de memória específica do componente é chamado de state.

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

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

Veja como uma galeria de imagens usa e atualiza o state 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}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i>
        by {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Hide' : 'Show'} details
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </>
  );
}

Ready to learn this topic?

Leia State: A Component’s Memory para aprender como lembrar um valor e atualizá-lo na interação.

Read More

Render e commit

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

Imagine que seus componentes são cozinheiros na cozinha, montando pratos saborosos a partir de ingredientes. Nesse cenário, o React é o garçom que faz pedidos de clientes e lhes traz seus pedidos. Esse processo de solicitação e atendimento da UI tem três etapas:

  1. Triggering uma renderização (entregando o pedido do jantar para a cozinha)
  2. Rendering o componente (preparando o pedido na cozinha)
  3. Committing para o DOM (colocando o pedido na mesa)
  1. React as a server in a restaurant, fetching orders from the users and delivering them to the Component Kitchen.
    Trigger
  2. The Card Chef gives React a fresh Card component.
    Render
  3. React delivers the Card to the user at their table.
    Commit

Illustrated by Rachel Lee Nabors

Ready to learn this topic?

Leia Render e Commit para aprender o ciclo de vida de uma atualização da UI.

Read More

State como um snapshot

Ao contrário das variáveis regulares do JavaScript, o React state se comporta mais como um snapshot. Definê-lo não altera a variável de state que você já possui, mas sim aciona uma re-renderização. Isso pode ser surpreendente no começo!

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

Este comportamento ajuda a evitar bugs sutis. Aqui está um pequeno aplicativo de bate-papo. Tente adivinhar o que acontece se você pressionar “Enviar” primeiro e depois alterar o destinatário para Bob. Cujo nome aparecerá no alert cinco segundos depois?

import { useState } from 'react';

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

  function handleSubmit(e) {
    e.preventDefault();
    setTimeout(() => {
      alert(`You said ${message} to ${to}`);
    }, 5000);
  }

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

Ready to learn this topic?

Leia State como um Snapshot para aprender por que o state aparece “fixo” e imutável dentro dos event handlers..

Read More

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

Este componente está com erro: 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>Score: {score}</h1>
    </>
  )
}

State como um Snapshot explica por que isso está acontecendo. Definir o state solicita uma nova re-renderização, mas não o altera no código já em execução. Portanto, 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 state. Observe como substituir setScore(score + 1) por setScore(s => s + 1) corrige o botão “+3”. Isso permite que você enfileire várias atualizações de state.

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>Score: {score}</h1>
    </>
  )
}

Ready to learn this topic?

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

Read More

Atualizando objetos no state

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

Geralmente, você usará a sintaxe de espalhamento ... para copiar objetos e arrays que você deseja alterar. Por exemplo, atualizar um objeto aninhado pode se parecer com isso:

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>
        Name:
        <input
          value={person.name}
          onChange={handleNameChange}
        />
      </label>
      <label>
        Title:
        <input
          value={person.artwork.title}
          onChange={handleTitleChange}
        />
      </label>
      <label>
        City:
        <input
          value={person.artwork.city}
          onChange={handleCityChange}
        />
      </label>
      <label>
        Image:
        <input
          value={person.artwork.image}
          onChange={handleImageChange}
        />
      </label>
      <p>
        <i>{person.artwork.title}</i>
        {' by '}
        {person.name}
        <br />
        (located in {person.artwork.city})
      </p>
      <img
        src={person.artwork.image}
        alt={person.artwork.title}
      />
    </>
  );
}

Se copiar objetos em código se tornar 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 State para aprender como atualizar objetos corretamente.

Read More

Atualizando arrays no state

Arrays são outro tipo de objetos JavaScript mutáveis que você pode armazenar no state e devem ser tratados como somente leitura. Assim como com objetos, quando você deseja atualizar uma array armazenada no state, você precisa criar uma nova (ou fazer uma cópia de uma existente) e, em seguida, definir o state para usar a nova 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>Art Bucket List</h1>
      <h2>My list of art to see:</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 tornar 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 State para aprender como atualizar arrays corretamente.

Read More

O que vem a seguir?

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

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