A transição do Higher-Order Component pattern para o React Hooks pattern
Tags: react, javascript
Published: 2024-03-30 15:15:00

> _**NOTA:** Este artigo é apenas uma tradução. A fonte dele está no rodapé._

A reutilização é um princípio essencial a ser seguido no desenvolvimento e programação de software. React (também conhecido como React.js ou ReactJS) é uma biblioteca de user interface declarativa baseada em componentes que nos fornece uma base sólida para que possamos utilizá-la. O Higher-Order Component pattern (HOC) do React é uma ótima maneira de alcançar isso.

Neste artigo, aprenderemos sobre o Higher-Order Component pattern e depois passaremos para a compreensão do React Hooks pattern com muitos exemplos. Por favor, [leia este artigo](https://blog.openreplay.com/understanding-the-container-component-pattern-with-react-hooks) se você for novo no React ou estiver ansioso para entender como o Container Component Pattern funciona.

## O Higher-Order Component(HOC) Pattern

Higher-Order Component é um pattern para estender e reutilizar um componente existente. Você não deseja duplicar a lógica do código entre os componentes. É um trabalho extra e causa situações adversas como problemas de manutenção de bugs. Além disso, você pode querer aprimorar um componente e usá-lo para uma finalidade completamente diferente.

Conforme definido na [documentação oficial do React](https://legacy.reactjs.org/docs/higher-order-components.html),

> Um higher-order component é uma função que pega um componente e retorna um novo componente.

Assim, você pode visualizar o uso do HOC como,

```js
const EnhancedComponent = HOC(WrappedComponent);
```

Aqui o `HOC` é o higher-order component que recebe `WrappedComponente` retorna um novo `EnhancedComponent`. Agora vamos aprender mais sobre HOC com exemplos.

Vamos considerar alguns componentes do React para listar filmes de um banco de dados e mostrar análises sobre esses filmes, respectivamente.

- **MovieListWithHOC**: um componente que lista as informações do filme, como nome, imagem da capa, nome do diretor e assim por diante.
- **MovieAnalyticsWithHOC**: um componente que mostra a análise dos dados do filme.

Esses componentes possuem um requisito padrão, ou seja, buscar os dados do banco de dados. Vamos supor que temos uma API (URI: [https://json-faker.onrender.com/movies](https://json-faker.onrender.com/movies)) que podemos solicitar para buscar os dados do filme como resposta.

A lógica de buscar os dados do filme usando a API é uma necessidade comum para ambos os componentes. Mas espere, não queremos repetir a lógica de busca de dados em ambos os componentes. Em vez disso, usaremos o HOC pattern para reutilizar a lógica de busca e aprimorar os componentes.

Primeiro, vamos criar um HOC com a lógica de busca de dados do filme.

```js
import React from "react";

// Cria um HOC que recebe um component como um argument
const withFetch = (WrappedComponent) => {
    
  // Cria um Class Component que irá
  // melhorar o WrappedComponent 
  class WithFetch extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        movies: [],
      };
    }

    componentDidMount() {
      // Fetch os dados do filme realizando uma chamada de API  
      fetch("https://json-faker.onrender.com/movies")
        .then((response) => response.json())
        .then((data) => {
          this.setState({ movies: data.movies });
        });
    }

    render() {
      return (
        <>
          {this.state.movies.length > 0 && (
            <WrappedComponent movies={this.state.movies} />
          )}
        </>
      );
    }
  };
  
  // É opcional configurar o display name do
  // component mas é útil quando realizarmos o debug do HOC.
  WithFetch.displayName = `WithFetch(${WithFetch.name})`;
  
  // Finalmente, retorna o componente
  return WithFetch;
};

export default withFetch;
```

Algumas coisas a serem observadas aqui:

- Criamos um componente React (uma função JavaScript regular) withFetch que usa um componente (WrappedComponent) como argumento.
- O componente withFetch retorna um componente (class componente).
- Criamos uma variável de "state" chamada **movies** e a atualizamos usando a resposta da chamada da API no lifecycle method componentDidMount.
- Agora dê uma olhada na parte JSX do componente. Aqui aprimoramos a passagem de dados dos filmes de WrappedComponent como props.
- Finalmente, retornamos o componente (Class Component). A parte principal deste componente é o WrappedComponent aprimorado com os dados do filme.

É poderoso porque agora você pode passar um "presentational component" (as pessoas o chamam de "componente idiota"!) para o Higher-Order component withFetch para obter de volta um componente totalmente operacional com dados. A melhor parte são vários "presentational components" (digamos, um para listar filmes e outro para mostrar análises), que podem usar o mesmo HOC para buscar dados e representá-los de maneira diferente. Não é incrível?

Vamos usar o HOC para criar um componente de listagem de filmes,

```js
import MovieContainer from "../movies/MovieContainer";
import "../movies/movies.css";
import withFetch from "./MovieWrapper";

const MovieListWithHOC = (props) => {
  const { movies } = props;
  return (
    <div className="movie-container">
      <h2>Movie List - With HoC</h2>
      <MovieContainer data={movies} />
    </div>
  );
};

export default withFetch(MovieListWithHOC);
```

Como mostra o código acima, MovieListWithHOC é um presentational component aprimorado com HOC (withFetch).

Observe o export, que diz,

```js
export default withFetch(MovieListWithHOC);
```

Assim, o componente MovieListWithHOC obtém os dados do filme como props e os passa para outro componente chamado MovieContainer para renderizar os dados. Você pode dar uma olhada em um exemplo de implementação do componente MovieContainer [aqui](https://github.com/atapas/react-play/blob/release/openreplay-hoc-hooks/src/movies/MovieContainer.js).

Da mesma forma, podemos ter outro componente para mostrar análises de filmes usando os mesmos dados do filme,

```js
import MovieAnalytics from "../movies/MovieAnalytics";
import "../movies/movies.css";
import withFetch from "./MovieWrapper";

const MovieAnalyticsWithHOC = (props) => {
  const { movies } = props;
  return (
    <div className="movie-container">
      <h2>Movie Analytics - With HoC</h2>
      <MovieAnalytics data={movies} />
    </div>
  );
};

export default withFetch(MovieAnalyticsWithHOC);
```

Você pode dar uma olhada em um exemplo de implementação do componente MovieAnalytics [aqui](https://github.com/atapas/react-play/blob/release/openreplay-hoc-hooks/src/movies/MovieAnalytics.js).

Espero que você tenha achado o uso do HOC impressionante. É um excelente uso da reutilização de código. Muitas bibliotecas baseadas em React (incluindo React) como Redux, React-Router usam esse pattern.

Antes de passarmos para a próxima seção para entender os patterns do React Hook, esteja ciente de algumas advertências mencionadas na documentação do React.

## O React Hooks pattern

React Hooks são uma nova inclusão na biblioteca na versão 16.8. Antes disso, não podíamos usar o "state" e alguns outros recursos do React fora de class components. No React, podemos criar um componente usando classes ou funções JavaScript. Com o advento dos Hooks, agora é muito mais fácil adotar a programação funcional no React.

Com componentes funcionais e hooks juntos, podemos separar a preocupação de gerenciar o state e os side-effects de um componente para um hook. O hook pode isolar o "plumbing" de todos esses componentes para mantê-lo pure.

O React fornece vários hooks standards prontos para uso. O useState, useEffect, useMemo, useCallback, useRef, são alguns para citar. Também podemos criar hooks personalizados para separar a lógica reutilizável e possíveis side-effects de um componente. Se você é novo no React Hooks, aqui está um pequeno [vídeo](https://youtu.be/CvNvRaS3u60) para apresentar os fundamentos do React Hooks.

Tudo bem, aprendemos que o HOC ajuda na reutilização, assim como o React Hook. Então, podemos converter um HOC em um React Hook e usá-lo? Assim estaremos totalmente "funcionais", certo? Sim, vamos fazer isso.

É uma convenção nomear um React Hook começando com `use` (sim, a letra **u** está em minúsculo!). Criaremos um hook customizado chamado `useFetch`.

```js
import { useState, useEffect } from "react";

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(url);
      const json = await response.json();
      setData(json);
      setLoading(false);
    };
    fetchData();
  }, [url]);

  return [loading, data];
};

export { useFetch };
```

Vamos entender o código acima passo a passo:

O hook `useFetch` é uma função JavaScript que usa uma URL como argumento. É a API URL para buscar dados. O hook é muito flexível para buscar dados usando qualquer API URL passada. Passaremos o API URL para buscar os dados do filme em nosso caso.

O hook usa duas variáveis ​​de state loading e data. As variáveis ​​de state são declaradas e rastreadas quanto a alterações usando o hook `useState`.

> **useState**: ajuda a gerenciar o state local para que o React possa preservar e usar o state entre novas renderizações. O hook useState retorna o valor do state atual e uma função para atualizar o valor do state. Também podemos passar o valor do state inicial para o hook `useState()`.

Em seguida, o hook useEffect gerencia os side-effects de fazer a chamada da API e buscar os dados. Depois de concluído, atualizamos as variáveis ​​de state indicando que o carregamento dos dados foi feito e "fetched".

> **useEffect**: O hook useEffect ajuda na execução de side-effects. O side-effect pode ser qualquer coisa responsável pela alteração do state da sua application. Um exemplo típico de side-effect é fazer uma chamada de API e alterar o state local para preservar os dados de resposta.

Finalmente, retornamos ambas as variáveis ​​de state em um array.

Ótimo, não é hora de usar o hook em um componente para obter os dados do filme e renderizar na IU. Que tal usá-lo para o mesmo MovieAnalytics componente que usamos antes?

Vamos criar um componente chamado `MovieAnalyticsWithHook` para demonstrá-lo,

```js
import { useFetch } from "../hooks/useFetch";
import MovieAnalytics from "../movies/MovieAnalytics";

const MovieAnalyticsWithHook = () => {
  const MOVIE_URI = "https://json-faker.onrender.com/movies";
  const [loading, data] = useFetch(MOVIE_URI);
  return (
    <div>
      <h1>Movie Analytics with Hooks</h1>
      {
          loading ? 
          <h2>Loading...</h2> : 
          <MovieAnalytics data={data.movies} />
      }
    </div>
  );
};

export default MovieAnalyticsWithHook;
```

Chamamos o hook `useFetch` passando a API URI do filme no código acima. Ele retorna os valores `loading` e `data` que realizamos destructuring em duas variáveis.

```js
const [loading, data] = useFetch(MOVIE_URI);
```

A seguir, na parte JSX, usamos `loading` e `data` para mostrar a análise do filme.

É isso. Espero que você também tenha gostado de aprender sobre os Reach Hooks.

## Fonte

[Artigo](https://blog.openreplay.com/the-transition-from-higher-order-component-pattern-to-react-hooks-pattern/) escrito por **Tapas Adhikary**.