Objeto: Componentes React com Classes, detalhes JSX
Principais termos técnicos abordados: JSX, class, React, ReactDOM, JavaScript, HTML
Requisitos para as instruções funcionarem: ter cumprido 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
Completando passos e exercícios da receita anterior, fizemos funções que funcionavam como marcas HTML e que podiam ser, inclusive, usadas como tal. No mundo React, essas funções são o que chamamos de componentes. No início do React, os componentes eram, exclusivamente, classes, e hoje podem ser classes ou funções.
Vamos ver nessa receita como implementar componentes através de classes em componentes, forçando um ou outro erro no meio do caminho para entendermos melhor alguns detalhes de JavaScript e JSX.
Vamos partir de um possível código HTML resultante da implementação da receita anterior e de seus exercícios.
Crie um arquivo HTML com o seguinte código:
<!DOCTYPE html>
<html>
<head>
<title>ReceitaReact - Classes</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 type="text/babel">
const TabelaBebidas = () => {
return
<h1>Tabela de Bebidas</h1>
<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>
}
ReactDOM.render(
<TabelaBebidas/>
,
document.getElementById("app"))
</script>
</body>
</html>
Salve o arquivo, abra no navegador e inspecione o código.
Você deve encontrar uma mensagem de erro pouco lisonjeira. Aqui no meu navegador veio essa aqui:
Uncaught SyntaxError: Inline Babel script: Adjacent JSX elements must be wrapped in an enclosing tag
Na verdade, a mensagem é bem clara e quer dizer, em linguagem simplificada, que todo código XML/HTML que viermos a utilizar precisa estar dentro de uma única marca. No nosso caso, o retorno tem duas marcas, um <h1> e um <table>.
Para ter apenas uma a gente ou apaga uma dessas marcas ou põe uma marca "container" que venha a ter ambas como filhas. Vamos nessa segunda abordagem.
Faça com que o código de sua página fique como a seguir:
<!DOCTYPE html>
<html>
<head>
<title>ReceitaReact - 2</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 type="text/babel">
const TabelaBebidas = () => {
return
<div>
<h1>Tabela de Bebidas</h1>
<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>
}
ReactDOM.render(
<TabelaBebidas/>
,
document.getElementById("app"))
</script>
</body>
</html>
Note a marca <div> que engloba as marcas <h1> e <table>.
- Isso deve nos livrar daquele erro, certo?
Certo. Mas só daquele erro.
Prossigamos.
Salve o arquivo, abra no navegador, inspecione o código.
Você deve encontrar outro erro, dessa vez. E menos lisonjeiro ainda.
Aqui está algo como:
Uncaught Error: TabelaBebidas(...): Nothing was returned (...)
Bem está escrito que nada foi retornado na função TabelaBebidas() [para ser renderizado].
- Como assim, Professor??? A gente retornou um balai de HTML...
Aparentemente, sim. Entretanto, como diz aquele ditado milenar japonês: "Em JavaScript, <enter> é ponto-e-vírgula". Assim, como nossa marca <div> se inicia uma linha depois do return, quando a função for chamada, ela vai simplesmente retornar "nada", como se fosse uma função que retorna void em outras linguagens. Isso é um detalhe do JavaScript e não do JSX, um detalhe que pode torar suas pernas, porque seu código aparentará estar correto mas não estará. Se você for no console do inspetor de código, definir a função...
function f(){
return
5
}
...e depois chamar...
f()
...o retorno da função vei ser undefined e não 5. Esse problema é que, com o uso do JSX, por questões de legibilidade, pode ser que você, sem nem perceber, inicie sua marca na linha seguinte ao return, especialmente se ela tiver muitos filhos. Como corrigir isso? Vamos lá...
Abra um parêntese na MESMA linha do return e feche-o em qualquer canto após finalizada sua marca mais abrangente. O código completo fica como a seguir:
<!DOCTYPE html>
<html>
<head>
<title>ReceitaReact - 2</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 type="text/babel">
const TabelaBebidas = () => {
return (
<div>
<h1>Tabela de Bebidas</h1>
<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>
)
}
ReactDOM.render(
<TabelaBebidas/>
,
document.getElementById("app"))
</script>
</body>
</html>
Pronto, agora deve exibir a tabela normalmente, depois de muita peleja...
- Sim, mas e os componentes de classes?
Calma, jovem. Vai ser bem simples, por isso acrescentei esses outros dois pontos.
À classe.
Crie uma classe que herda da classe React.Component, sobrescreva o método render(), fazendo-o retornar as marcas a serem renderizadas pelo React (o mesmo que a função TabelaBebidas retorna). E apague a função TabelaBebidas ou a renomeie. O código completo fica assim:
<!DOCTYPE html>
<html>
<head>
<title>ReceitaReact - 2</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 type="text/babel">
class TabelaBebidas extends React.Component{
render(){
return (
<div>
<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>
)
}
}
const TabelaBebidas__ = () => {
return (
<div>
<h1>Tabela de Bebidas</h1>
<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>
)
}
ReactDOM.render(
<TabelaBebidas/>
,
document.getElementById("app"))
</script>
</body>
</html>
Note que, no ReactDOM.render(), a gente pode usar o </TabelaBebidas> do mesmo jeito, seja o componente uma função, seja o componente uma classe.
A gente encontra autores e autores, alguns excelentes e outro nem tanto, escrevendo que a opção entre componentes de classes e componentes de função é meramente de preferência pessoal.
No princípio, havia coisas que os componentes de função não podiam fazer e os de classe podiam, como ter um estado. Com os React Hooks, isso não existe mais.
Até aí, seria, teoricamente, uma questão de preferência pessoal.
Considere, no entanto, que as funções são mais simples - menos burocráticas, em termos de código.
E que frameworks como NextJS se baseiam bastante nelas.
1. Escreva outros componentes de classe e acrescente-os à interface de sua página
2. Que tal se o conteúdo de sua marca <h1> do componente TabelaBebidas fosse uma propriedade do seu componente de classe? Desta forma, se a gente escrevesse <TabelaBebidas titulo="Tabela de Cana"> o conteúdo da marca <h1> seria Tabela de Cana, e assim por diante. Modifique seu código para acomodar essa mudança.