Que os Hooks do ReactJS não são mais novidade, já sabemos, mas eles ainda causam um pouco de espanto - principalmente se você está chegando agora e não entende o motivo de alguns componentes serem escritos com classe e outros com função. Se esse é o seu caso, assim como já foi o meu, pegue uma cadeira e sente-se confortavelmente que eu tentarei sanar as suas dúvidas.
Para entender sobre a diferença entre componente funcional e componente de classe dentro do React, tenha em mente que componentes são os responsáveis por permitir a divisão da interface em partes independentes e reutilizáveis.
A maior diferença entre ambas as formas é a sintaxe. A utilização das funções é também mais reforçada devido ao código ser mais enxuto e a possibilidade de utilização dos hooks, ainda que haja funcionalidades específicas para os componentes de classe.
Não é difícil encontrar diversos projetos com componentes sendo criados a partir de classes, com a atualização do React para a versão 16.8, o padrão recomendado se tornou a function, já que nas versões anteriores os states não podiam ser utilizados em componentes funcionais. Dessa maneira, quando havia a necessidade de alteração ou de manipulação de algum state, era necessário que o componente fosse alterado para uma classe.
Vou mostrar como este processo era realizado anteriormente, e qual é a maneira recomendada hoje. Assim, você conseguirá ver na prática quais mudanças ocorreram.
Caso você queira testar os exemplos apresentados aqui, o projeto pode ser criado através do comando: npx create-react-app nomedoprojeto
Por padrão, as versões mais recentes criam a aplicação utilizando os modelos de função.
Antes, a sintaxe mais facilmente encontrada era algo parecido com essa:
import React from 'react';
class App extends React.Component {
render() {
return(
Olá,
);}
}
export default App;
Agora, podemos criar um outro componente, a fim de reaproveitar o código. Veja:
import React from 'react';
class MyText extends React.Component{
render(){
return
Olá Pessoa/mundo
}
}
class App extends React.Component {
render() {
return(
);}
}export default App;
A nível de instrução, todo componente definido pelo usuário tem que, obrigatoriamente, começar com letra maiúscula.
O componente pode ser chamado várias vezes, sem problema nenhum. Aí, você me pergunta: “Por que eu iria chamar o mesmo componente várias vezes com o mesmo texto?” Que bom que você perguntou! Na verdade, eu só fiz isso para apresentar as props.
Segundo a própria documentação do ReactJS, as props ou propriedades são os parâmetros que você passa para o seu componente. Por exemplo:
Elas podem ter qualquer valor, exatamente como na função, quando eu invocar as props dentro do meu componente, será recebido tudo o que foi passado.
Dentro do componente as props são lidas assim:
return
Olá, {this.props.name}
O Exemplo ficará assim:
import React from 'react';
class MyText extends React.Component{
render(){
return
Olá, {this.props.name}
}
}
class App extends React.Component {
render() {
return(
);}
}
export default App;
Também é possível que esse meu componente aceite outros parâmetros vindos como props, exemplo:
import React from 'react';
class MyText extends React.Component{
render(){
return
Olá {this.props.local}! Eu sou uma {this.props.gender} chamada {this.props.name}
}
}
class App extends React.Component {
render() {
return(
);}
}
export default App;
Entendido o conceito das props, é importante saber que o há outro método utilizado em classe para cuidar dos states, ou seja, estados do meu componente.
Aqui começamos a entender como podemos alterar o valor ou estado das informações que temos no nosso componente. Este método é similar às props, porém privado.
Para explicar, trarei um exemplo um pouco diferente. Ao usar o this.state, como já mencionado anteriormente, é necessário que os componentes sejam declarados como classe, visto que estamos trabalhando desta forma podemos seguir adicionando o construtor na classe onde o estado do componente será alterado.
Conforme a documentação, o constructor é um método especial para criar e iniciar um objeto criado pela classe. Só pode existir um método com o nome constructor dentro da classe.
É importante entender que você não poderá utilizar o this em um construtor sem antes ter chamado o construtor pai. Isso é feito utilizando a palavra-chave super.
import React from 'react';
class MyText extends React.Component{
render(){
return
Clicado: {this.props.text}
}
}
class MyButton extends React.Component{
render(){
return(
{this.props.handleClick(this.props.button)} }>
{this.props.button}
)
}
}
class App extends React.Component {
constructor(props){
super(props);
this.state = {
mylabel: ""
}
}
setMyLabel = (mylabel) => {
this.setState({ mylabel });
}
render() {
return(
);}
}
export default App;
Entendendo o código:
Partindo para a chamada desse método e renderização do componente, é passado o método responsável por atualizar o estado da mesma maneira que as props, ou seja, o nome e o valor, que no exemplo é: handleClick={this.setMyLabel} .
Dentro do componente MyButton será chamado a props que contêm este método, e também será passado como parâmetro o novo valor. Assim: onClick={() => {this.props.handleClick(this.props.button)} }>
Se você já está familiarizado com o React pode estar se perguntando sobre o ciclo de vida e seus métodos, mas isso podemos deixar para um próximo post. Que tal?
Até aqui entendido, de uma maneira mais direta e simples sobre a funcionalidade dos componentes de classe, vamos virar a chave e entender sobre outra maneira de declarar um componente.
Nos componentes declarados como função temos algumas diferenças de sintaxe e os hooks, o que, atualmente, possibilita e facilita na sua usabilidade.
A base da função seria chamada dessa forma:
function App(props) {
return (
Olá, {props.name}
);
}
export default App;
Ou seja, utilizando as props sem problemas, porém sem mudança e manipulação através do this.state e sem a necessidade do método render.
Eles permitem que você use o state e outros recursos sem criar uma classe para isso. As maravilhas da modernidade, não é mesmo?
As principais motivações para implantação dos hooks segundo o núcleo do React foram: A dificuldade em entender alguns componentes quando muito complexos e extensos, e a confusão para os novos desenvolvedores, onde a maneira de utilizar os estados acabava se tornando uma barreira no aprendizado do framework.
Caso você queira saber de maneira mais detalhada sobre o que levou a essa mudança, aqui tem um link super interessante.
Uma definição bem resumida encontrada na documentação é:
“Hooks são funções que permitem a você ‘ligar-se’ aos recursos de state e ciclo de vida do React a partir de componentes funcionais. Hooks não funcionam dentro de classes — eles permitem que você use React sem classes.”
Então, vamos de exemplo. Quando dizemos que atualmente os componentes se tornaram mais simples de se escrever, é devido a situações similares a esta:
import React, { useState } from 'react'
function App() {
const [name, setName] = useState("Forasteiro");
return (
Olá {name}
setName("Viajante do tempo")}>
Click Me
);
}
export default App;
Primeiro, há a importação do useState diretamente do React. Nós já entendemos que ele é um hook que nos permite manipular o state dentro de funções, mas o que ele faz?
Ele declara uma array onde o primeiro elemento manterá o estado inicial, que serve para preservar alguns valores entre as chamadas de função e o segundo elemento responsável por manipular os valores atuais.
A maneira como é informado o estado inicial da variável será informando-o como parâmetro para o useState().
Observando o exemplo, há a declaração de um array com dois valores, seguido do useState e um valor passado por parâmetro.
const [name, setName] = useState("Forasteiro")
O valor que está sendo passado por parâmetro será o valor inicial do estado. Ele será atribuído ao primeiro elemento dentro do array, ou seja, ao name. O segundo elemento setName será o responsável por alterar o estado do componente.
Para apresentar os valores que no nosso exemplo, caso fosse com classe seria this.state.name. Agora, com função é utilizado apenas o name diretamente.
A atualização desse estado também não poderia ser complicada, no caso do exemplo estamos lidando com um botão, então no click deste botão é chamado apenas o setName que irá atualizar o estado atual, passando por parâmetro qual será o novo valor.
Na prática, a diferença para o componente de classe, com o mesmo comportamento, seria assim:
import React from 'react';
class ExemploClasse extends React.Component {
constructor(props){
super(props)
this.state = {
name: "Forasteiro"
}
}
render(){
return (
Olá {this.state.name}
this.setState({ name: "Viajante do tempo"})}>
Click Me
);
}
}
export default ExemploClasse
Ou seja, um pouco maior e consequentemente mais confuso para quem inicia no React.
Agora que ambas as formas foram apresentadas, fica a seu critério escolher a que faz mais sentido para você. Lembrando que até a publicação deste artigo o React não tem planos de descontinuar as classes, então você não precisa se preocupar, já que a inserção dos hooks aconteceu em fevereiro de 2019. Não é algo novo, mas ainda causa um pouco de confusão. Por esse motivo, o intuito deste artigo foi apenas apresentar as duas formas e demonstrar algumas diferenças entre elas.