React Hooks personalizados: por que precisamos de um contexto
Tags: react, javascript, programming
Published: 2022-12-21 14:04:14

React Hooks Personalizados é uma maneira muito conveniente de encapsular a lógica e passar os dados para a árvore de renderização.

As regras para React Hooks personalizados são bastante simples:

> _Um Hook personalizado é uma função JavaScript cujo nome começa com "use" e que pode chamar outros Hooks._


## Hooks Puros

Considere esta implementação muito simples de um hook personalizado, que encapsula a utilização de permissões para um serviço de documento compartilhado:

```jsx

import React from 'react';
import { Permissions, usePermissions } from '@hooks/permissions';

const useIsPermitted = () => {
  // usePermissions é outro hook customizado que retorna um array de permissões
  const permissions = usePermissions();
  return {
    isEditPermitted: permissions.includes(Permissions.EDIT_PERMISSION),
  }
```

Como você pode ver, este hook é bastante simples, ele faz uso de outro hook personalizado que retorna uma **array** de permissões e as mapeia em uma expressão bastante simples de permitido/não permitido. Isso permitirá uma melhor reutilização de código em toda a aplicação e nos ajudará a evitar a duplicação de código em todos os lugares onde precisamos dessa verificação:

```jsx
 
import React from 'react';

export const EditButton = () => {
  const { isEditPermitted } = useIsPermitted();
  return <button disabled={!isEditPermitted}>Editar</button>
}
```

O único problema é que essa lógica será executada a cada nova renderização. No caso de uma array pequena, é insignificante, mas se for uma array grande, teremos problemas. Uma simples adição ao hook personalizado pode resolver esse problema:

```jsx
import { Permissions, usePermissions } from '@hooks/permissions';

const useIsPermitted = () => {
  // usePermissions é outro hook customizado que retorna um array de permissões
  const permissions = usePermissions();
  return useMemo(() => ({
    isEditPermitted: permissions.includes(Permissions.EDIT_SITE_PERMISSION),
  }), [permissions]);
}
```
Dessa forma, as permissões serão verificadas novamente, somente quando a array de **permissões for alterada**.

Caso resolvido! Será mesmo?

[A documentação](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook) oficial do React, pode nos dar uma dica do que pode estar errado com essa abordagem:

> **Dois componentes usando o mesmo Hook compartilham o estado?**
    Não. Hooks personalizados são um mecanismo para _reutilizar a lógica de estado_ (como configurar uma assinatura e lembrar o valor atual), mas toda vez que você utiliza um hook personalizado, todos os estados e efeitos dentro dele são totalmente isolados.

> **Como um Hook personalizado obtém o estado isolado?** 
    Cada chamada para um hook, obtém o estado isolado. Como chamamos useFriendStatus diretamente, do ponto de vista do React, nosso componente apenas chama useState e useEffect. Além disso, podemos chamar useState e useEffect várias vezes em um componente, e eles serão completamente independentes.

Isso significa que, se chamarmos **useIsPermitted** de dois componentes diferentes (ou até mesmo duas vezes do mesmo componente), a lógica será executada para cada instância da invocação **useIsPermitted**, mesmo que usemos **useMemo** dentro.

## Hooks personalizados com contexto

Uma solução para isso, seria combinar um hook personalizado com um contexto. 

Vamos revisar nossa implementação de hook **useIsPermitted**:

```jsx
  import React, { createContext, useMemo } from 'react';
import { Permissions, usePermissions } from '@hooks/permissions';

export const PermissionsContext = createContext({});

export const IsPermittedProvider = ({ children }) => {
  const permissions = usePermissions();

  const permissionsDictionary = useMemo(() => ({
    isEditPermitted: permissions.includes(Permissions.EDIT_SITE_PERMISSION),
  }), [permissions]);
  
  return (
    <PermissionsContext.Provider
      value={permissionsDictionary}
    >
      {children}
    </PermissionsContext.Provider>
  );
};
```

```jsx

import React, { useContext } from 'react';
import { PermissionsContext } from '@contexts/permissions';

export const useIsPermitted = () => useContext(PermissionsContext);
```

Agora a lógica tem como escopo um provider de contexto específico. Isso significa que se utilizarmos este provider apenas uma vez na raiz da aplicação, a lógica será executada apenas uma vez:

```jsx
import React from 'react';
import { App } from './app';

<PermissionsProvider>
  <App />
</PermissionsProvider> 
```

É claro que podemos decidir colocar o provider mais abaixo na árvore de renderização, para que a lógica seja executada somente quando uma parte relevante for renderizada.

## Quando usar o que

Embora um hook com contexto pareça ser uma solução mais robusta em termos de desempenho e consumo de memória, isso não significa que você deva sempre seguir essa abordagem. 

Há lugar para ambas as abordagens, mas é importante entender as implicações de cada uma delas.

Aqui está uma pequena lista de verificação que o ajudará a decidir sobre a abordagem correta:

### Use Hooks quando:
    
- O estado do hook personalizado deve ser isolado (diferente por instância).

- Nenhum cálculo pesado é executado no hook personalizado.

- Você só usa o hook uma vez na aplicação (como forma de passar dados para a árvore de renderização).

### Use Hook com Contexto quando:
  - A subárvore intreira deve compartilhar o estado do hook.
  
  - Há um cálculo pesado executado dentro do hook e você deseja que ele seja executado o mais raramente possível.

Espero que você tenha gostado do artigo, comente se tiver alguma dúvida.