DEV Community

Cover image for Passado e futuro?
Thiago Pederzolli Machado da Silva
Thiago Pederzolli Machado da Silva

Posted on

Passado e futuro?

Olá!

Se você chegou direto aqui e não tem base nenhuma sobre Reat, aconselho a voltar e ler o React e seus três mosqueteiros primeiro para se contextualizar melhor.

Agora se você já é familiarizado com os conceitos de componentes, propriedades e estados em React, boa leitura!💜

-------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Dando continuidade aos textos sobre conceitos básicos de React, temos a questão de criação de componentes. A forma de como os componentes são criados em React dividem-se em duas fases, o início e evolução da ferramenta através do Class Component, até o momento que foi implementado os Hooks no React que tornaram viáveis utilizar algumas funcionalidades essenciais do React com Functional Components.

De sua origem até o surgimento dos Hooks, os estados só eram acessíveis através de Class Components. E essa implementação dos Hooks é algo recente,não vá lendo isso e achando que “ah, então agora só estudar Functional Components que ta tranquilo.”. Você irá quebrar a cara em diversas oportunidades de inserção no mercado.

Talvez não tenha mais tanto sentido na criação de Class Components, porém você ainda pode lidar com diversos códigos legados e outras ferramentas que criam a base dos componentes em forma de Class.

Imutabilidade:

Um detalhe importante de lembrar antes de entrar na diferenciação de como os componentes funcionam, é a questão da imutabilidade do React.

Por que é importante lembrar disso agora? Porque nos exemplos falarei sobre os estados e esse conceito é muito importante para entender a atualização de estados.

O que esse conceito em poucas palavras é a ideia de que um valor de uma variável nunca deve ser alterada diretamente, precisamos recuperar esse valor, alterá-lo em uma nova variável juntando o novo valor e então atribuir essa variável ao método de atualizar o estado, garantindo assim um novo espaço de memória com essa nova informação, sem afetar diretamente aquela informação anterior que já ocupava outro espaço na memória.

Class Components:

Baseado na premissa de Objects do React, o Class é um objeto que estende o objeto Component, um objeto nativo do React responsável por ter as principais propriedades e métodos para criação de componentes, por isso começamos ele com a seguinte sintaxe:

import React, { Component } from 'react';

class ExampleComponent extends Component {
  render() {
    return (
      <h1>Olá Mundo!</h1>
    )
  }
}

export default ExampleComponent;
Enter fullscreen mode Exit fullscreen mode

Um dos principais métodos que ele herda é o render(), responsável por retornar o JSX daquele componente. Porém podemos criar novos métodos para utilizar neste componente. Porém, por padrão, os métodos criados não são vinculados a classe, para isso, precisamos invocar outro método herdado que é o constructor(), ele nos permitirá fazer o bind() do método criado com a instância, que é representada pelo termo this, quando você faz o bind do método com o this, você está vinculando aquele método à instância do componente associado com o elemento da DOM que representa esse componente.

Se você criar um método e usar diretamente no render, ele será executado, porém se ele não for de uso imediato, como um método que irá fazer uma atualização de estado, precisamos garantir o bind, para isso precisamos do método constructor() e dentro dele do método super(). Dentro do constructor(), primeiro declaramos o super() que irá garantir que nosso componente continuará herdando os métodos do Component e também será possível adicionar novos métodos.

Feito isso podemos fazer o bind() de nossa função e teremos algo assim:

import React, { Component } from 'react';

class ExampleComponent extends Component {
  constructor() {
    super();
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange() {
    console.log('atualiza um estado!');
  }

  render() {
    return (
      <h1>Olá Mundo!</h1>
    )
  }
}

export default ExampleComponent;
Enter fullscreen mode Exit fullscreen mode

Lembrando que precisamos colocar o this antes do nome da função também para vincular ao método da nossa instância.

Sobre as props, o acesso é da mesma forma que se acessa os estados, através do this para vincular a instância atual, caso precise usar uma props para atualizar algum valor de estado inicial na hora de criar o state do componente, lembre-se passar as props por parâmetro para o constructor(props) e super(props):


import React, { Component } from 'react';

class ExampleComponent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      newState: this.props.data
    }
  }

  render() {
    return (
      <h1>Olá Mundo!</h1>
    )
  }
}

export default ExampleComponent;
Enter fullscreen mode Exit fullscreen mode

Por fim, outra questão muito importante do método constructor() é que é nele que conseguimos construir nossos estados dentro de um Class Component. A criação de estados é bem diferente de uma forma de criação de componente, no caso de Class Components, construimos dentro do constructor e sua atualização se dá pelo mesmo método, vou mostrar o exemplo clássico do contador com Class Component e Functional Component para que seja possível compreender como criar e atualizar o estado.

Em um Class Component teríamos um código assim:

import React, { Component } from 'react';

class Counter extends Component {
  constructor() {
    super();
    this.state = {
      count: 0
    }
    this.increment = this.increment.bind(this);
  }

  increment() {
    this.setState({
      count: this.state.count + 1,
    })
  }

  render() {
    return (
      <div>
        <h1>{count}</h1>
        <button
          type="button"
          onClick={this.increment}
        >
          Aumentar em 1
        </button>
      </div>
    );
  }
}

export default Counter;
Enter fullscreen mode Exit fullscreen mode

Outro detalhe relevante da criação de componentes, ai tanto para Class quanto Functional é no return, que o JSX só permite o retorno de um elemento HTML por componente. Isso não quer dizer que você, literalmente, só pode retornar um elemento HTML por componente, mas o retorno final deve estar englobado dentro de um elemento só, por isso vocês podem perceber que o retorno do contador tem uma tag

e uma tag , mas ambas estão englobadas por uma tag .

Outra questão relevante sobre os componentes é que eles possuem ciclos de vida, que era de domínio do Class Component e foi permitido ao Functional Component repetir os comportamentos com a implementação de Hooks.

Os ciclos de vida referem-se aos momentos de renderização do componente e os métodos para trabalhar esses momentos acredito que são auto-explicativos. Sobre a implementação deles em código acredito que cabe um texto a parte para explicações, mas os principais são:

  • componentDidMount para executar uma ação após o componente ser inserido no DOM;
  • shouldComponentUpdate para avaliar se uma atualização do componente deve ou não acontecer naquele momento;
  • componentDidUpdate para executar uma ação após o componente ser atualizado;
  • componentWillUnmount para realizar uma ação antes de o componente ser desmontado.
  • Functional Components:

    Componentes funcionais cresceram em termos de utilização após o lançamento dos Hooks, que permitem uso de estados e ciclos de vida. A sintaxe também acaba sendo mais simples, acabam-se as preocupações de bind, pois acaba a questão da herança de objetos, agora trabalhamos com funções, então a base do componente acaba sendo simples:

import React from 'react';

function ExampleComponent() {
  return (
    <h1>Olá Mundo</h1>
  )
}

export default ExampleComponent;

A partir dai podemos trabalhar com os estados a partir de um hook específico para isso que é o useState(). Focando no que é importante para nós, vamos nos atentar ao fato de o useState ser um método que irá, principalmente, retornar um array, que o que nos importa são os dois primeiros elementos desse array.

O primeiro elemento do nosso array será a variável que irá armazenar o valor do nosso estado inicial e quando ele for atualizado. O segundo elemento será o método responsável por atualizar o valor desse estado. Por convenção, os nomes desses elementos são, para o primeiro, o nome do estado que é algo relacionado ao dado que irá armazenar e, para o segundo, o prefixo set seguido do nome do estado. FIcará mais compreensível no exemplo abaixo.

Perceba que, diferentemente do Class Component, onde todos estados ficavam dentro do objeto this.state e tudo era atualizado pelo this.setState(), no Functional Component, cada estado possui uma variável própria e terá seu método para ser atualizado:

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>{count}</h1>
      <button
        type="button"
        onClick={increment}
      >
        Aumentar em 1
      </button>
    </div>
  );
}

export default Counter;

Perceba, também, que é passado como argumento para o useState o valor inicial da variável count.

Sobre os ciclos de vida com o Functional Component, temos outro Hook, que é useEffect(), ele também é um método que recebe dois argumentos, o primeiro é a função que deve ser executada e o segundo é um array de dependências.

Dependências essas que definirão o ciclo de funcionamento do useEffect(), por exemplo, se passarmos o array vazio, ele funcionará como o componentDidMount, executando a função passada para ele apenas uma vez. Isso é ótimo para momentos que trabalhamos com requisições API para atualizar dados do nosso componente.

Trago aqui parte do código do primeiro módulo do Ignite, trilha React, da Rocketseat como um breve exemplo de como funcionaria essa questão de utilizar o useEffect() com um ciclo de vida similar ao componentDidMount para atualizar um estado com o retorno de uma requisição API.

import React, { useEffect, useState } from "react";

function RepositoryList() {
  const [repositories, setRepositories] = useState([]);

  useEffect(() => {
    fetch('https://api.github.com/orgs/rocketseat/repos')
      .then(response => response.json())
      .then(data => setRepositories(data));
  }, []);

  return (
    <section>
      <h1>Repositórios</h1>
    </section>
  )
}

export default RepositoryList;

Esse código está incompleto em termos de funcionamento, porque precisaria explicar outros conceitos e já me alonguei nas explicações deste texto. Mas o foco dele é a sintaxe para entender o uso do useEffect().

Se você já estudou sobre promises, async/await, você pode estar se perguntando porque o uso do .then() ao invés de o async/await. O useEffect não aceita diretamente uma função assíncrona, então se você optar por trabalhar com async/await, seria necessário fazer uma função anônima que terá em seu corpo a função assíncrona que fará a requisição, tendo algo similar a:

import React, { useEffect, useState } from "react";

function RepositoryList() {
  const [repositories, setRepositories] = useState([]);

  useEffect(() => {
    const fetchRepos = async() => {
      const response = await fetch(`
         https://api.github.com/orgs/rocketseat/repos`);
      const data = await response.json();
      setRepositories(data);
    }

    fetchRepos();
  }, []);

  return (
    <section>
      <h1>Repositórios</h1>
    </section>
  )
}

export default RepositoryList;

E, ao meu ver, essas são as principais diferenças entre os métodos de criação de um componente que eu me deparei até o momento. Concorda? Acredita que há mais alguma diferença relevante? Me equivoquei em alguma informação? Por favor, não hesite em colaborar com qualquer informação, meu foco é contribuir com a comunidade, mas principalmente encontrar uma forma de documentar meu aprendizado e levar minhas compreensões ao maior alcance possível, para que possamos reforçar os entendimentos corretos e reparar entendimentos errôneos.

Top comments (0)