DEV Community

Cover image for React Hooks personalizados: por que precisamos de um contexto
Ivan Trindade
Ivan Trindade

Posted on

React Hooks personalizados: por que precisamos de um contexto

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:


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

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:


import React from 'react';

export const EditButton = () => {
  const { isEditPermitted } = useIsPermitted();
  return <button disabled={!isEditPermitted}>Editar</button>
}
Enter fullscreen mode Exit fullscreen mode

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:

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

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

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

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

export const useIsPermitted = () => useContext(PermissionsContext);
Enter fullscreen mode Exit fullscreen mode

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:

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

<PermissionsProvider>
  <App />
</PermissionsProvider> 
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)