Pular para o conteúdo

a

Introdução à biblioteca React

Agora, começaremos a nos familiarizar com provavelmente o tópico mais importante deste curso, a biblioteca React. Vamos começar criando uma aplicação React simples, conhecendo os conceitos-chave de React.

A maneira mais simples de começar é usando uma ferramenta chamada create-react-app. É possível (mas não necessário) instalar o create-react-app em sua máquina se a ferramenta npm instalada junto com o Node estiver na versão 5.3, pelo menos.

Durante o curo, você também pode utilizar a nova ferramenta frontend chamada Vite, se desejar. O create-react-app ainda é a ferramenta recomendada pelo time do React e é por isso que continua sendo a ferramenta padrão para configurar um projeto React neste curso. Leia aqui como o time React enxerga o futuro das ferramentas React.

Vamos criar uma aplicação chamada part1 e navegar até o seu diretório.

npx create-react-app part1
cd part1

A aplicação é executada da seguinte forma:

npm start

Por padrão, a aplicação é executada no localhost, porta 3000, no endereço http://localhost:3000.

Seu navegador padrão deve ser automaticamente aberto. Abra imediatamente o console do navegador. Além disso, abra um editor de texto para que você possa ver o código e a página web ao mesmo tempo na tela:

código e navegador lado a lado

O código da aplicação reside no diretório src. Vamos simplificar o código padrão para que o conteúdo do arquivo index.js fique assim:

import React from 'react'
import ReactDOM from 'react-dom/client'

import App from './App'

ReactDOM.createRoot(document.getElementById('root')).render(<App />)

E o arquivo App.js fique assim:

const App = () => (
  <div>
    <p>Olá, mundo!</p>
  </div>
)

export default App

Os arquivos App.css, App.test.js, index.css, logo.svg, setupTests.js e reportWebVitals.js podem ser excluídos, pois não são necessários em nossa aplicação neste momento.

Componente

O arquivo App.js agora define um componente React com o nome App. O comando na linha final do arquivo index.js

ReactDOM.createRoot(document.getElementById('root')).render(<App />)

renderiza seu conteúdo dentro do elemento div, definido no arquivo public/index.html, com o valor de id 'root'.

Por padrão, o arquivo public/index.html não contém nenhum marcador HTML que seja visível para nós no navegador:

<!DOCTYPE html>
<html lang="ptbr">
  <head>
      conteúdo não mostrado ...
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

Você pode até tentar adicionar algum HTML ao arquivo, no entanto, ao usar React, todo o conteúdo que precisa ser renderizado é geralmente definido como "componentes React".

Vamos dar uma olhada mais de perto no código que define o componente:

const App = () => (
  <div>
    <p>Olá, mundo!</p>
  </div>
)

Como você provavelmente já adivinhou, o componente será renderizado como uma tag div, que envolve uma tag p contendo o texto "Olá, mundo!".

Tecnicamente, o componente é definido como uma função JavaScript. O código a seguir também é uma função (que não recebe nenhum parâmetro):

() => (
  <div>
    <p>Olá, mundo!</p>
  </div>
)

A função é, então, atribuída a uma (variável) constante App:

const App = ...

Existem algumas maneiras de definir funções em JavaScript. Aqui usaremos as funções de seta (arrow functions), que são descritas em uma versão mais recente de JavaScript conhecida como ECMAScript 6, também chamada de ES6.

Por conta da função consistir em apenas uma única expressão, usamos uma notação abreviada, que representa este trecho de código:

const App = () => {
  return (
    <div>
      <p>Olá, mundo!</p>
    </div>
  )
}

Em outras palavras, a função retorna o valor da expressão.

A função que define o componente pode conter qualquer tipo de código JavaScript. Modifique seu componente da seguinte maneira:

const App = () => {
  console.log('Olá do componente!')
  return (
    <div>
      <p>Olá, mundo!</p>
    </div>
  )
}

export default App

E observe o que acontece no console do navegador:

console do navegador com uma seta mostrando o log com a mensagem "Hello from component"

A primeira regra do desenvolvimento web front-end:

Mantenha o console aberto o tempo todo.

Vamos repetir juntos: Prometo manter o console aberto o tempo todo durante este curso e pelo resto da minha vida quando estiver desenvolvendo para a web.

Também é possível renderizar conteúdo dinâmico dentro de um componente.

Modifique o componente da seguinte maneira:

const App = () => {
  const hoje = new Date()
  const a = 10
  const b = 20
  console.log(hoje, a+b)

  return (
    <div>
      <p>Olá, mundo! Hoje é {hoje.toString()}</p>
      <p>
        {a} mais {b} é {a + b}
      </p>
    </div>
  )
}

Qualquer código JavaScript dentro das chaves é avaliado e o resultado desta avaliação é incorporado no lugar definido no HTML produzido pelo componente.

Note que você não deve remover a linha no final do componente:

export default App

A exportação não é mostrada na maioria dos exemplos do material do curso. Sem a exportação, o componente e a aplicação inteira desmoronam.

Você se lembrou da sua promessa de deixar o console aberto? O que foi impresso?

JSX

Parece que os componentes React estão retornando marcações HTML. No entanto, não é esse o caso. A maior parte da estrutura de componentes React é escrita usando JSX (JavaScript Syntax Extension [Extensão de Sintaxe para JavaScript]). Embora o JSX pareça com HTML, estamos lidando com uma maneira de escrever JavaScript. Por baixo dos panos, o JSX retornado por componentes React é compilado em JavaScript.

Depois da compilação, nossa aplicação fica assim:

const App = () => {
  const hoje = new Date()
  const a = 10
  const b = 20
  return React.createElement(
    'div',
    null,
    React.createElement(
      'p', null, 'Olá, mundo! Hoje é ', hoje.toString()
    ),
    React.createElement(
      'p', null, a, ' mais ', b, ' é ', a + b
    )
  )
}

A compilação é gerenciada pelo Babel. Projetos criados com create-react-app são configurados para compilar automaticamente. Vamos aprender mais sobre esse tópico na Parte 7 deste curso.

Também é possível escrever React como "JavaScript puro" sem usar JSX. Embora não seja recomendável.

Na prática, o JSX é muito parecido com HTML com a diferença de que, com o JSX, é possível inserir facilmente conteúdo dinâmico escrevendo código JavaScript dentro de chaves. A ideia do JSX é bastante semelhante a muitas linguagens de modelos, como Thymeleaf usado junto ao Java Spring, que são usadas em servidores.

JSX é "semelhante a XML" (Extensible Markup Language [Linguagem de Marcação Extensível]), o que significa que todas as tags precisam ser fechadas. Por exemplo, uma nova linha é um elemento vazio, que em HTML pode ser escrito da seguinte maneira:

<br>

Mas ao escrever em JSX, a tag precisa ser fechada:

<br />

Múltiplos componentes

Vamos modificar o arquivo App.js da seguinte forma (obs.: a exportação na parte inferior é omitida nestes exemplos, tanto agora quanto no futuro. Ela ainda é necessária para que o código funcione):

const Hello = () => {  return (    <div>      <p>Olá, mundo!</p>    </div>  )}
const App = () => {
  return (
    <div>
      <h1>Olá a todos!</h1>
      <Hello />    </div>
  )
}

Definimos um novo componente Hello e o usamos dentro do componente App. Naturalmente, um componente pode ser usado várias vezes:

const App = () => {
  return (
    <div>
      <h1>Olá a todos!</h1>
      <Hello />
      <Hello />      <Hello />    </div>
  )
}

Escrever componentes em React é fácil, e utilizando combinação de componentes mesmo uma aplicação mais complexa pode ser mantida de forma organizada. De fato, uma das filosofias fundamentais do React é criar aplicações a partir de muitos componentes que são especializados e reutilizáveis.

Outra forte convenção é a ideia de um componente root chamado App no topo da árvore de componentes da aplicação. No entanto, como aprenderemos na Parte 6, há situações em que o componente App não é exatamente a raiz (root), mas é envolto em um componente utilitário apropriado.

props: passando dados para componentes

É possível passar dados para componentes usando as chamadas props (properties [propriedades]).

Vamos modificar o componente Hello da seguinte maneira:

const Hello = (props) => {  return (
    <div>
      <p>Olá {props.nome}</p>    </div>
  )
}

Agora, a função que define o componente tem um parâmetro "props". Como argumento, o parâmetro recebe um objeto, que possui campos correspondentes a todas as "props" que o usuário do componente define.

As props são definidas da seguinte forma:

const App = () => {
  return (
    <div>
      <h1>Olá a todos!</h1>
      <Hello nome='George' />      <Hello nome='Daisy' />    </div>
  )
}

É possível haver um número arbitrário de props e seus valores podem ser strings "hard-coded" (dados ou estruturas em um código que não podem ser alterados sem modificar manualmente o programa) ou resultados de expressões JavaScript. Se o valor da prop é obtido usando JavaScript, ele deve ser envolvido em chaves.

Vamos modificar o código para que o componente Hello use duas props:

const Hello = (props) => {
  console.log(props)  return (
    <div>
      <p>
        Olá {props.nome}, você tem {props.idade} anos      </p>
    </div>
  )
}

const App = () => {
  const nome = 'Peter'  const idade = 10
  return (
    <div>
      <h1>Olá a todos!</h1>
      <Hello nome='Maya' idade={26 + 10} />      <Hello nome={nome} idade={idade} />    </div>
  )
}

As props enviadas pelo componente App são os valores das variáveis, isto é, o resultado da avaliação da expressão de soma e de uma string comum.

O componente Hello também registra o valor do objeto props no console.

Eu espero genuinamente que seu console esteja aberto. Se não estiver, lembre-se do que você prometeu:

Eu prometo manter o console aberto o tempo todo durante este curso e pelo resto da minha vida quando estiver desenvolvendo para a web.

Desenvolvimento de software é difícil. Fica ainda mais difícil se não estiver usando todas as ferramentas possíveis, como o console web e a impressão de depuração com console.log. Profissionais usam ambos o tempo todo, e não há razão alguma para que um iniciante não adote o uso desses métodos maravilhosos que tornam a vida muito mais fácil.

Alguns lembretes

O React foi configurado para gerar mensagens de erro bastante claras. Mesmo assim, você deve, pelo menos no começo, avançar com passos bem curtos e tendo certeza de que cada mudança funciona como desejado.

O console deve estar sempre aberto. Se o navegador relatar erros, não é aconselhável continuar escrevendo mais código, esperando por milagres. Em vez disso, você deve tentar entender a causa do erro e, por exemplo, voltar ao estado anterior de funcionamento:

captura de tela de um erro prop indefinido

Como já mencionamos, é possível e recompensador escrever comandos console.log() (que imprimem no console) ao programar em React.

Além disso, tenha em mente que os nomes de componentes React devem estar com a primeira letra em maiúsculo. Se você tentar definir um componente da seguinte forma:

const footer = () => {
  return (
    <div>
      Aplicação de Saudações criado por <a href='https://github.com/mluukkai'>mluukkai</a>
    </div>
  )
}

E usá-lo desta forma:

const App = () => {
  return (
    <div>
      <h1>Olá a todos!</h1>
      <Hello nome='Maya' idade={26 + 10} />
      <footer />    </div>
  )
}

A página não vai exibir o conteúdo definido dentro do componente Footer e, em vez disso, React cria apenas um elemento footer vazio, ou seja, o elemento HTML incorporado em vez do elemento React personalizado com o mesmo nome. Se você mudar a primeira letra do nome do componente para maiúsculo, o React cria um elemento div definido no componente Footer, que é renderizado na página.

Note que o conteúdo de um componente React (normalmente) precisa conter um elemento raiz (root). Se, por exemplo, tentarmos definir o componente App sem o elemento div externo:

const App = () => {
  return (
    <h1>Olá a todos!</h1>
    <Hello nome='Maya' idade={26 + 10} />
    <Footer />
  )
}

o resultado é uma mensagem de erro.

captura de tela de múltiplos erros de elementos-raiz

Usar um elemento raiz não é a única opção viável. Um array (vetor) de componentes também é uma solução válida:

const App = () => {
  return [
    <h1>Olá a todos!</h1>,
    <Hello nome='Maya' idade={26 + 10} />,
    <Footer />
  ]
}

Porém, definir o componente raiz da aplicação não é algo particularmente sábio a se fazer, e deixa o código com uma aparência um pouco feia.

Por conta do elemento raiz ser compulsório, temos elementos div "extras" na árvore DOM. Isso pode ser evitado usando fragmentos, ou seja, envolvendo os elementos a serem retornados pelo componente com um elemento vazio:

const App = () => {
  const nome = 'Peter'
  const idade = 10

  return (
    <>
      <h1>Olá a todos!</h1>
      <Hello nome='Maya' idade={26 + 10} />
      <Hello nome={nome} idade={idade} />
      <Footer />
    </>
  )
}

Agora, a aplicação compila com sucesso, e a DOM gerada pelo React não contém mais o elemento "div" extra.

Não renderize objetos

Considere uma aplicação que imprime os nomes e idades de nossos amigos na tela:

const App = () => {
  const amigos = [ 
      { nome: 'Peter', idade: 4 },
      { nome: 'Maya', idade: 10 },
    ]

  return (
    <div>
      <p>{amigos[0]}</p>
      <p>{amigos[1]}</p>
    </div>
  )
}

export default App

No entanto, nada aparece na tela. Venho tentando encontrar o problema no código há 15 minutos, mas não consigo descobrir onde o problema poderia estar.

Eu finalmente lembro da promessa que fizemos:

Eu prometo manter o console aberto o tempo todo durante este curso e e pelo resto da minha vida quando estiver desenvolvendo para a web.

O console grita em vermelho:

fullstack content

O núcleo do problema é que: Objetos não são válidos como elementos-filho React, isto é, a aplicação tenta renderizar objetos e falha novamente.

O código tenta renderizar as informações de um amigo da seguinte maneira:

<p>{amigos[0]}</p>

E isso causa um problema, porque o item a ser renderizado dentro das chaves é um objeto.

{ nome: 'Peter', idade: 4 }

Em React, elementos individuais renderizadas dentro das chaves devem ser valores primitivos, como números ou strings.

A solução é a seguinte:

const App = () => {
  const amigos = [ 
      { nome: 'Peter', idade: 4 },
      { nome: 'Maya', idade: 10 },
    ]

  return (
    <div>
      <p>{amigos[0].nome} {amigos[0].idade}</p>
      <p>{amigos[1].nome} {amigos[1].idade}</p>
    </div>
  )
}

export default App

O nome do amigo é renderizado separadamente dentro das chaves:

{amigos[0].nome}

Também a idade:

{amigos[0].idade}

Após corrigir o erro, limpe as mensagens de erro do console clicando em 🚫 e, em seguida, recarregue o conteúdo da página e garanta que não haja mensagens de erro exibidas.

Uma adição ao lembrete anterior: React também permite que arrays sejam renderizados se conterem valores elegíveis para renderização (como números ou strings). Então, o seguinte programa funcionaria, embora o resultado não seja o que desejamos:

const App = () => {
  const amigos = [ 'Peter', 'Maya']

  return (
    <div>
      <p>{amigos}</p>
    </div>
  )
}

Nesta parte, nem vale a pena tentar usar a renderização direta de tabelas. Voltaremos a discutir isso na próxima parte.