HOC Pattern - React
Tags: javascript, designpatterns, react, webdev
Published: 2024-06-07 14:17:36

No segundo artigo desta série de como fazer uso de _design patterns_ em projetos, especificamente usando React, vamos considerar - um dos meus preferidos - o _pattern_ **H**igh **O**rder **C**omponent **(HOC)**.

Em poucas palavras, o **HOC** é uma técnica em que uma função recebe um componente em seu argumento e esta função contém uma lógica que afeta o componente passado por parâmetro, para então renderizá-lo.

O **HOC** nos permite encapsular uma mesma lógica a ser utilizada em múltiplos componentes sem que precisássemos replicar a lógica em todos eles. Pode ser uma lógica que envolve estilos, permissões, visibilidade de micro-componentes dentro de um componente, além de outros casos de uso.

## Show me the code

Imagine que você tenha o seguinte componente:

```tsx
import React from "react";

export const Me = () => {
  const isLoggedIn = useAuth();

  async function handleGetMe() {
    const response = await fetch('/profile');
    const data = await response.json();

    return data;
  }

  if (!isLoggedIn) {
    return (
       <div>
          Você precisa estar logado para ver essas informações.
       </div>
    );
  }

  const profile = handleGetMe();

  return (
    <div>
       <h2>{profile.name}</h2>
       <p>{profile.job}</p>
    </div>
  );
};
```

Como podemos ver acima, esse componente possui uma lógica de permissionamento. Se o usuário estiver logado, é possível ver os seus dados, mas se não estiver, recebe uma mensagem. 

No entanto, temos duas observações a fazer:

1. Não é tão interessante ter uma lógica assim dentro do componente que tem como objetivo realmente mostrar algo na tela;

2. Pode ser que essa lógica precise ser usada em outros componentes e nesse formato teria que replicar a lógica nos outros componentes também.

Com certeza há várias formas de construir uma solução para isso, de forma que pudéssemos reutilizar a lógica em múltiplos componentes sem precisar repetir o contexto.

O **HOC** é uma dessas soluções. Veja um exemplo de criação e uso desse _design pattern_.

```tsx
export default function renderIfAuthenticated(Component: JSX.ElementType) {
   return (props) => {
      const isLoggedIn = useAuth();
      
      if (!isLoggedIn) {
         return (
            <div>
               Você precisa estar logado para ver essas informações.
            </div>
         );
      }
      
      return <Component {...props} />;
   }
}
```

Agora, nós conseguimos centralizar a lógica dentro de uma função **HOC**. Em outras palavras, transferimos a responsabilidade para uma única função. Recebemos o componente por parâmetro e o renderizamos apenas se a condição que definimos for verdadeira, nesse caso. E para utilizar podemos fazer assim:

```tsx
import { renderIfAuthenticated } from "../hocs/renderIfAuthenticated";

export function Me() {
   async function handleGetMe() {
      const response = await fetch('/profile');
      const data = await response.json();

      return data;
   }

   const profile = handleGetMe();

   return (
      <div>
         <h2>{profile.name}</h2>
         <p>{profile.job}</p>
      </div>
   );
}

const MeRenderWithAuth = renderIfAuthenticated(Me);
export default renderIfAuthenticated;
```

Note que o componente em si, se mantém o mesmo. A linha de código que faz o encapsulamento no **HOC** é esta:

```tsx
const MeRenderWithAuth = renderIfAuthenticated(Me);
```

Assim, podemos reutilizar essa lógica em outros componentes que vão fazer uso da mesma lógica de permissionamento.

Vamos ver outro caso de uso. Por exemplo, ao aplicar os mesmos estilos em alguns componentes e extender estilos que serão diferentes para cada um.

```tsx
export function renderWithStyles(Component: JSX.Element) {
  return (props) => {
    const style = {
      backgroundColor: "#0B1215",
      color: "#F5F5F5",
      ...props.style,
    };

    return <Component {...props} style={style} />;
  };
}
```

Aplicando o HOC em múltiplos componentes:

**Button**

```tsx
import { renderWithStyles } from "../hocs/renderWithStyles";

export const Button = (props) => (
  <button style={{ fontSize: "20px", ...props.style }}>Enviar</button>
);

const StyledButton = renderWithStyles(Button);
export default StyledButton;
```

**Cardboard**

```tsx
import { renderWithStyles } from "../hocs/renderWithStyles";

export const Cardboard = (props) => (
  <div style={{ fontSize: "20px", fontFamily: "Roboto", ...props.style }}>Enviar</div>
);

const StyledCardboard = renderWithStyles(Cardboard);
export default StyledCardboard;
```

Basicamento, o que fizemos acima é o que é feito na biblioteca de estilos CSS-In-JS muito conhecida chamada **Styled Components**. Veja o exemplo abaixo tirado da documentação:

```js
const Button = styled.button`
  color: #BF4F74;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid #BF4F74;
  border-radius: 3px;
`;

// Aplicação do HOC
const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;
```

O **TomatoButton** vai herdar os estilos do **Button** e acrescentar ou substituir propriedades exclusivas para ele.

---

### Considerações finais


**Reusabilidade** - Os **HOC's** nos permitem encapsular funcionalidades que são compartilhadas em vários componentes, promovendo o reuso do código;

**Separation of Concerns (SOC)** - Ajudam a manter a separação de responsabilidades da aplicação, assim os componentes podem focar no objetivo de uso deles;

**Abstração de Código** - Como os **HOC's** abstraem a lógica em comum de um ou mais componentes, fica mais canalizado e fácil de entender e dar manutenção;

**Composição** - Podemos combinar vários **HOC's** para compor uma funcionalidade complexa dentro dos componentes. Um exemplo de uso de algo assim, mas mais antigo é do `MapStateToProps` e do `MapDispatchToProps` do `React-Redux`:

```js
export default connect(mapStateToProps, mapDispatchToProps)(AppWrapper(MyComponent))
```

---

Portanto, fazer uso dos **HOC's** tornarão sua aplicação mais fácil de manter e entender, por promover um código limpor e reusável. Assim, suas aplicações React serão mais eficientes. 

Mas como todos os padrões de projeto, é preciso pensar no caso de uso, na necessidade e até mesmo na complexidade do projeto para decidir se seria interessante o uso de **HOC's**.