Desmistificando React Hooks — useCallback

Dec 03 2022
Neste artigo, exploraremos quando e como usar o gancho useCallback do React e um erro cometido pela maioria dos desenvolvedores juniores.
Introdução Se quiser acompanhar em seu IDE local, você pode encontrar o GitHub Repo aqui. Igualdade Referencial é um conceito fundamental em JavaScript e Ciência da Computação como um todo.

Começando

Se quiser acompanhar em seu IDE local, você pode encontrar o GitHub Repo aqui .

  • clone
  • cd client
  • npm i
  • npm start

Igualdade Referencial é um conceito fundamental em JavaScript e Ciência da Computação como um todo. Então, vamos começar com uma demonstração dele em ação.

Incorporei capturas de tela ao longo do artigo para facilitar o uso em dispositivos móveis. Se você estiver na área de trabalho e o tiver clonado, execute referentialEquality.jspara observar a saída ou apenas jogue com o trecho JSFiddle incorporado abaixo.

Ao avaliar se o integer 1é estritamente igual ao integer 1, o console imprime true. Isso ocorre porque, bem… o integer 1 é estritamente igual ao integer 1.

os inteiros são estritamente iguais

Vemos o mesmo resultado ao avaliar duas strings.

as cordas são estritamente iguais

Obviamente, este sempre será o caso para dois tipos de dados primitivos do mesmo valor.

Agora, e as estruturas de dados? Por exemplo, dois literais de objeto com os mesmos pares chave/valor? E os literais de objetos vazios?

os objetos não são estritamente iguais

Por que isso imprimiria false? Ao comparar se esses dois literais de objeto são estritamente iguais, o JavaScript usa seus respectivos endereços de memória .

Em outras palavras, esses dois objetos podem conter os mesmos valores , mas não estão referenciando o mesmo objeto . Eles parecem iguais, mas ocupam dois espaços diferentes na memória .

O mesmo se aplica se você estiver comparando dois literais de objeto, dois literais de matriz ou duas funções!

as matrizes não são estritamente iguais

Para demonstrar isso melhor, definiremos uma função func, que retorna uma função anônima que, por sua vez, retorna outra coisa ( como um elemento JSX ).

criando um componente funcional fingido

Atribuiremos então duas funções diferentes, firstRendere secondRender, igual ao valor retornado por func.

atribuindo nossas definições de função

Pense funccomo seu componente funcional React, enquanto firstRenderé uma função dentro dele na primeira renderização e secondRenderé uma função dentro dele na segunda renderização.

Mesmo que firstRendere secondRenderretornem o mesmo valor e sejam atribuídos a partir da mesma definição, eles não possuem igualdade referencial . Como resultado, toda vez que o componente renderiza, ele redefine essa função.

nossas duas funções não são estritamente iguais

Infelizmente, em JavaScript, não é fácil imprimir esses endereços de memória como em Python, mas para uma explicação um pouco mais aprofundada de referência versus valor, dê uma olhada neste artigo do freeCodeCamp.

Este tópico pode ficar denso e você não precisa dar uma aula sobre ele esta noite. Então, por enquanto, lembre-se:

  • tipo de dado primitivo ===tipo de dado primitivo
  • estrutura de dados !==estrutura de dados.

Código inicial

Depois de ativar nosso aplicativo, abra o BookDetails.jsxcomponente e salve novamente. A primeira coisa que podemos notar em nosso servidor de desenvolvimento React é algo comum WARNINGque os jovens desenvolvedores tendem a ignorar. À medida que você atinge a força de trabalho e começa a escrever código para produção, seus linters serão ainda mais rígidos do que o que está embutido no create-react-app. WARNINGSirá virar para ERRORS, e alguns linters não permitirão que você empurre antes de endereçá-los ERRORS.

Portanto, em vez de ignorá-lo, vamos descobrir como tratá-lo.

Soluções propostas pelo React

NOTA: você pode primeiro precisar salvar novamente BookDetails.jsxpara criar esteWARNING

Se nos aprofundarmos no React Docs , podemos decodificar as soluções propostas semiconfusas para isso WARNINGda seguinte forma:

1. Inclua a definição da função dentro do arquivo useEffect.

  • Não podemos chamar essa função em outro lugar, a menos que a redefinamos.
  • Isso acionará useEffect toda vez que o estado ou os adereços mudarem, às vezes causando uma re-renderização infinita. E, em nosso caso, como estamos chamando nossa função de configurador de estado após uma chamada de API, isso pode sobrecarregar nossa API com infinitas solicitações de ponto de extremidade.
  • A função não será chamada.
  • Na primeira vez que o componente for renderizado, ele definirá nossa função, que acionará o useEffect, o que fará com que o componente seja renderizado novamente, o que redefinirá a função, o que acionará o useEffect, o que fará com que o componente seja renderizado novamente, o que irá redefinir a função…

A solução mais simples e preferida seria 'incluí-la', ou seja, mover a getBookDetailsdefinição da função para dentro do arquivo useEffect. Isso segue um princípio de Programação Orientada a Objetos conhecido como Encapsulamento .

Mas digamos que sabemos que precisamos chamar a função em outro lugar. Devemos redefini-lo mais tarde? Isso não é muito seco de nós.

Vamos alterar nosso array de dependências para incluir nossa referência de função. Agora você useEffectdeve ficar assim.

incluindo nossa função no array de dependências

E getBookDetailspermanece definido acima do useEffect.

a definição original da nossa função

Agora temos um novoWARNING

definir nossa função fora do useEffect enquanto a inclui na matriz de dependências causará uma re-renderização infinita

Insira o gancho useCallback

Resumindo, o useCallbackgancho permite que você armazene em cache, ou 'memoize', uma função entre as re-renderizações do seu componente. Ele executa uma tarefa semelhante a useMemo, cujas nuances abordaremos em outro artigo.

Se o âmago da questão for do seu interesse, você pode ler mais nos documentos do React .

Observe o aviso:

Você só deve confiar useCallbackcomo uma otimização de desempenho. Se o seu código não funcionar sem ele, encontre o problema subjacente e corrija-o primeiro. Então você pode adicionar useCallbackpara melhorar o desempenho.

useCallbackSintaxe

useCallbackA sintaxe de ' é muito semelhante à useEffectsintaxe de ' que já conhecemos. Olhe para os esqueletos de cada um.

os dois ganchos têm esqueletos sintáticos idênticos

A pequena diferença é com useEffect, dizemos à função anônima para executar nossa função enquanto com useCallback, atribuímos o valor de retorno a uma referência a ser chamada em outro lugar.

Usando useCallback

Primeiro, vamos importar useCallbackde 'react'. Em vez de adicionar uma nova linha, é melhor desestruturar junto com nossas outras importações.

itens desestruturados do mesmo pacote são melhor importados na mesma linha

Agora podemos atribuir getBookDetailsao valor retornado de uma useCallbackchamada de função.

atribuir o retorno de useCallback à nossa referência

Em seguida, adicionamos toda a sintaxe para useCallback. Lembre-se de sua matriz de dependência!

preencha o esqueleto de useCallback

Em nosso exemplo, precisamos asyncantes de nossos parâmetros.

adicionar assíncrono

E, finalmente, adicionamos a lógica de nossa função ao bloco de código.

adicione nossa lógica ao bloco de código de useCallback

Depois de salvar, obtemos ... outro arquivo WARNING.

o retorno de useCallback depende do valor de id

Por que nosso array de dependências deve rastrear a idvariável?

Vamos pensar sobre isso.

  • Se o valor de idmudar, getBookDetailsprecisa atingir um ponto final diferente, então o React deve redefini-lo. A definição de getBookDetailsliteralmente depende do valor de id.
versões acabadas de ambos os ganchos

E por fim, é isso! Vemos verde em nosso servidor React dev. Um linter feliz é um desenvolvedor sênior feliz. E um Desenvolvedor Sênior feliz é um feliz você!

Estou sempre procurando novos amigos e colegas. Se você achou este artigo útil e gostaria de se conectar, você pode me encontrar em qualquer uma das minhas casas na web.

GitHub | Twitter | Linkedin | Local na rede Internet

Recursos

  • Igualdade Referencial
  • Tipos de dados primitivos de JavaScript vs. estruturas de dados
  • Encapsulamento
  • useCallback
  • React Docs