Objeto: Introdução ao JSX
Principais termos técnicos abordados: JSX, React, ReactDOM, JavaScript, HTML
Requisitos para as instruções funcionarem: ter cumprido a Receita anterior
Requisitos para compreensão das instruções: noções bem básicas de programação JavaScript e de HTML
Como ler essa receita: instruções, dentro dos passos da receita e que requerem ação sua no computador, estarão escritas em cor azul. Comentários sobre os passos estarão em fonte normal, cor preta. Comandos, código-fonte, termos técnicos ou configuração explícita, estarão com fonte diferenciada.
Observação: eventuais instruções de terminal serão comandos Linux. Adapte caso esteja usando outro sistema operacional
Chicó: Cavalo bom como aquele eu nunca tinha visto. Uma vez corremos atrás de uma garrota, das seis da manhã até as seis da tarde, sem parar nem por um minuto, eu a cavalo, ele a pé. Fui derrubar a novilha já de noitinha, mas quando acabei o serviço e enchocalhei a rês, olhei ao redor, e não conhecia o lugar que estávamos. Tomei uma vereda que havia assim e saí tangendo o boi...
João Grilo: O boi? Não era uma garrota?
Chicó: Uma garrota e um boi.
João Grilo: E você corria atrás dos dois de uma vez?
Chicó (irritado): Corria, é proibido?
João Grilo: Não, mas eu me admiro eles correrem tanto tempo juntos, sem se apartarem. Como foi isso?
Chicó: Não sei, só sei que foi assim.
Completando passos e exercícios da receita anterior, é possível identificar alguns problemas. Vamos tentar resolvê-los aqui.
Edite o arquivo HTML da receita anterior e substitua o conteúdo dentro da derradeira marca <script> por...
ReactDOM.render(
React.createElement(
'table',
{id: 'tabela', border:'1'},
React.createElement(
'tr',
null,
React.createElement('th', null, 'Nome'),
React.createElement('th', null, 'Marca')
),
React.createElement(
'tr',
null,
React.createElement('td', null, 'Havana'),
React.createElement('td', null, 'Engenho Anísio Santiago')
)
),
document.getElementById("app"))
Salvando e abrindo o arquivo (no navegador), você deverá ver uma tabela de uma linha, com borda e cabeçalho...
...e o código HTML do <div> identificado por "app" (para vê-lo, você pode inspecionar o código da página) deve ficar assim...
<div id="app">
<table id="tabela" border="1">
<tr><th>Nome</th><th>Marca</th></tr>
<tr><td>Havana</td><td>Engenho Anísio Santiago</td></tr>
</table>
</div>
No entanto, por mais que aceitemos de boa fé que o React reserve grandes emoções para a gente, compor uma interface gráfica com essa hierarquização de chamadas a React.createElement() e transformar marcas HTML e suas propriedades em string's e objetos passados como parâmetros para funções parece aquela (nem sempre) boa e velha gambiarra.
E, de certa forma, é. Nesses moldes, ficaria bastante difícil produzir, ler, entender e modificar uma interface mais complexa.
Nossa vida seria mais fácil se a gente pudesse passar para o ReactDOM.render() o código HTML, direto, em vez de um encadeamento de chamadas a React.createElement().
Pareceria menos gambiarra.
Felizmente, como Chicó, que sempre resolvia uma mentira com outra maior ainda, em programação a gente às vezes resolve uma gambiarra com outra maior ainda.
Essa gambiarra maior ainda é o JSX - uma forma de embutir código HTML (ou XML, algo mais genérico) em código JavaScrip sem precisar usar strings ou coisas do tipo.
O JSX é excelente, e independente do React. Eu estou chamando de gambiarra apenas por tudo o que é preciso fazer por "debaixo dos panos" para a coisa dar certo. Mas o código, para a gente escrever, vai ficar muito melhor com o JSX. Tão melhor e tão bom que não deve demorar para termos navegadores suportanto isso nativamente.
Mas vamos ao que temos que fazer.
Precisamos, em nossa página, de uma biblioteca que "entenda" HTML dentro de código JavaScript. Essa biblioteca é o babel.
Baixe essa criança em seu computador:
curl -L https://unpkg.com/babel-standalone/babel.min.js >libs/babel.js
Edite seu arquivo HTML e acrescente o babel entre as bibliotecas "importadas". O código inteiro deve ficar assim e a parte em negrito é o código que foi acrescentado.
<!DOCTYPE html>
<html>
<head>
<title>React Um</title>
</head>
<body>
<div id="app">
</div>
<script src="libs/react.js"></script>
<script src="libs/react-dom.js"></script>
<script src="libs/babel.js"></script>
<script>
ReactDOM.render(
React.createElement(
'table',
{id: 'tabela', border:'1'},
React.createElement(
'tr',
null,
React.createElement('th', null, 'Nome'),
React.createElement('th', null, 'Marca')
),
React.createElement(
'tr',
null,
React.createElement('td', null, 'Havana'),
React.createElement('td', null, 'Engenho Anísio Santiago')
)
),
document.getElementById("app"))
</script>
</body>
</html>
Já que nossa página tem o babel que entende código HTML dentro do JavaScript, teoricamente, nossa derradeira marca <script> que está escrita...
<script>
ReactDOM.render(
React.createElement(
'table',
{id: 'tabela', border:'1'},
React.createElement(
'tr',
null,
React.createElement('th', null, 'Nome'),
React.createElement('th', null, 'Marca')
),
React.createElement(
'tr',
null,
React.createElement('td', null, 'Havana'),
React.createElement('td', null, 'Engenho Anísio Santiago')
)
),
document.getElementById("app"))
</script>
... poderia ser escrita...
<script>
ReactDOM.render(
<div id="app">
<table id="tabela" border="1">
<tr><th>Nome</th><th>Marca</th></tr>
<tr><td>Havana</td><td>Engenho Anísio Santiago</td></tr>
</table>
</div>
,
document.getElementById("app"))
</script>
Poderia sim, e ficaria bem mais legível.
O primeiro parâmetro do ReactDOM.render() é um código HTML ("o que" o React deve renderizar) e o segundo parâmetro continua sendo um elemento do DOM ("onde" o React deve renderizar). Mas precisamos de uma besteirinha a mais, é justamente aí onde entra a "gambiarra".
Os navegadores (ou, ao menos, muitas versões de muitos navegadores, a depender que quando você estiver lendo isso) não vão entender esse HTML dentro do JavaScript simplesmente porque não foram concebidos para rodar esse tipo de "mistura" (a especificação do JavaScript não inclui esse tipo de construto).
Aquele < da marca <div> do parâmetro para o ReactDOM.render( <div id="app">... ), aquele "sinalzinho de menor", o navegador deve tentar interpretá-lo justamente como isso, como o operador relacional, e a merda já vai começar daí.
No entanto, os navegadores foram concebidos para ignorar tipos desconhecidos de scripts. Vamos nos aproveitar disso.
Modifique a última marca <script> para que fique da seguinte forma:
<script type="text/babel">
ReactDOM.render(
<div id="app">
<table id="tabela" border="1">
<tr><th>Nome</th><th>Marca</th></tr>
<tr><td>Havana</td><td>Engenho Anísio Santiago</td></tr>
</table>
</div>
,
document.getElementById("app"))
</script>
Ao especificar type="text/babel" faremos com que o navegador ignore o código, pois ele não reconhece esse tipo de script. Entretanto, lembram do <script src="react/babel.js"></script> que escrevemos alguns passos atrás?
Pois é, código desta biblioteca sabe e vai processar o seu <script type="text/babel"> e transformá-lo em código JavaScript que o navegador sabe executar.
Esse processo é chamado de transpilação.
Notem que o JSX é uma tecnologia meio que independente do React, mas estamos usando as duas juntas para facilitar nossa vida, e é bem difícil que não se usem as duas juntas. Então JSX é tão interessante que simplesmente será a nosso "novo normal", podem deixar pra trás aqueles strings com código html dentro, porque aquilo, sim, é que é gambiarra. Ao escrevermos código em React, será código JSX e não JavaScript puro que usaremos para expressar nossas marcas.
Salve o arquivo, abra-o, inspecione o código.
A interface gráfica deve ficar do mesmo jeito da que construímos com as chamadas a React.createElement().
Pode ser que, na inspeção de código, você veja que seu navegador embombou com o código HTML (veja abas de Errors e Warnings) porque os <tr> devem ser escritos dentro de um <tbody> e os <th> dentro de um <thead>. Essa é a forma mais moderninha de se escrever o HTML <table>, apesar da forma que usamos funcionar perfeitamente.
Se fosse para resolver isso na versão "pura", apenas com os React.createElement(), iria ser um trabalho bem desagradável. Como agora a gente pode empurrar HTML dentro do código JavaScript, vai ficar bem mais fácil.
Altere o código dentro da derradeira marca <script>, de forma que esse código fique como a seguir:
ReactDOM.render(
<div id="app">
<table id="tabela" border="1">
<thead>
<tr><th>Nome</th><th>Marca</th></tr>
</thead>
<tbody>
<tr><td>Havana</td><td>Engenho Anísio Santiago</td></tr>
</tbody>
</table>
</div>
,
document.getElementById("app"))
Prontinho.
- Professor, se eu posso passar um código HTML como parâmetro, então eu também devo poder retornar um código HTML numa função, certo?
Certíssimo.
- Então não ficaria mais legível se eu tivesse uma função TabelaBebidas que retornasse aquele código HTML e, no ReactDOM.render(), eu simplesmente chamasse essa função no primeiro parâmetro?
Ficariazíssimo.
- E aí eu meio que teria criado uma marca HTML personalizada, a TabelaBebidas?
Meio-que-teriazíssimo.
- Faz aí, então, Professor.
Faz tu, macho. Vou botar isso como exercício.
1. Tente implementar uma função TabelaBebidas, que retorna o código HTML da tabela que construímos. Chame esse método no primeiro parâmetro do ReactDOM.render()
2. Altere a chamada do ReactDOM.render() para
ReactDOM.render(
<TabelaBebidas/>
,
document.getElementById("app"))
3. Faça uma outra função, de nome Cabecalho, que retorne uma marca h1 com o conteúdo "Minha tabela de bebidas". Incorpore essa chamada a sua página
4. Defina um array (fixo) de objetos JSON da sua escolha (pode ser cachaça, para manter o exemplo). Mostre uma tabela com informações desses objetos. O código de renderização da tabela deve ser parecido com o código da questão 2. No entanto, esse código de renderização deve ser executado apenas após o clique em algum link ou botão.
5. Bonitifique a tabela da questão anterior