DEV Community

Cover image for A transição do Higher-Order Component pattern para o React Hooks pattern
doug-source
doug-source

Posted on

A transição do Higher-Order Component pattern para o React Hooks pattern

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 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,

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,

const EnhancedComponent = HOC(WrappedComponent);
Enter fullscreen mode Exit fullscreen mode

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) 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.

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;
Enter fullscreen mode Exit fullscreen mode

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,

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);
Enter fullscreen mode Exit fullscreen mode

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

Observe o export, que diz,

export default withFetch(MovieListWithHOC);
Enter fullscreen mode Exit fullscreen mode

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.

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

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);
Enter fullscreen mode Exit fullscreen mode

Você pode dar uma olhada em um exemplo de implementação do componente MovieAnalytics aqui.

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 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.

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 };
Enter fullscreen mode Exit fullscreen mode

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,

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;
Enter fullscreen mode Exit fullscreen mode

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.

const [loading, data] = useFetch(MOVIE_URI);
Enter fullscreen mode Exit fullscreen mode

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 escrito por Tapas Adhikary.

Top comments (0)