Como posso identificar mais facilmente gargalos no desempenho de renderização do React?

Aug 15 2020

Estou tendo problemas para identificar gargalos no desempenho da renderização enquanto trabalho em um visualizador JSON. Com poucos elementos, tem um bom desempenho, mas a certa altura torna-se irritantemente lento.

Verificando o criador de perfil, parece que os elementos estão sendo renderizados com rapidez suficiente, mas notei alguns problemas que não tenho certeza de como resolver.

Visão geral

  • O aplicativo é um visualizador JSON que permite expandir/minimizar todos os elementos de uma só vez, bem como elementos individuais.
  • O desempenho é bom com poucos elementos, mas parece diminuir drasticamente à medida que o número de elementos aumenta.
  • Ao traçar o perfil do meu método de filtro de objeto performance.now(), bem como verificar o tempo de renderização no React DevTools, os números parecem bons. Posso estar interpretando errado.
  • Eu tentei usar React.memo()em elementos sem estado (particularmente a chave/valor que é o componente renderizado com mais frequência), mas não parece melhorar o desempenho visivelmente. Admito que não tenho certeza se entendi o raciocínio por trás da memorização de componentes React para implementar isso de forma útil.

Implementação

  • Atualmente, meu aplicativo carrega dados em um pai que alimenta um componente que carrega a árvore JSON usando um elemento recursivo.
  • O carregamento do feed JSON da URL altera o estado do componente pai, que é filtrado usando um método auxiliar que usa valores inseridos em um campo de entrada.

Problemas

Existem duas funcionalidades que reproduzem um tempo de resposta lento com documentos JSON (não tão grandes):

  • O botão expandir tudo
  • Os primeiros pressionamentos de tecla em uma consulta de filtro

Com a implementação atual, a filtragem e a expansão acionam uma display: nonealteração nos elementos filhos, e o comportamento me leva a acreditar que estou fazendo algo ineficiente para lidar com esse caso de uso.

Etapas de reprodução

O código está disponível aqui:https://codesandbox.io/s/react-json-view-4z348

Com uma compilação de produção aqui (sem desempenho melhor):https://csb-4z348.vercel.app/

Para reproduzir o problema, brinque com a função Expandir tudo (sinal de adição ao lado da entrada do filtro) e algumas entradas de filtro.

Em seguida, tente carregar um feed JSON com mais elementos (você pode testar no meu feed da API do GitHub ) e tente filtrar/expandir tudo. Observe o maior impacto no desempenho.

O que eu notei

  • Ao registrar useEffect, minimizar parece causar ~ 2x mais renderizações do que expandir tudo.
  • À medida que a entrada do filtro se torna mais específica, o desempenho (logicamente) melhora à medida que menos elementos são renderizados.

Pergunta

Embora eu agradeça um empurrão na direção certa para este caso específico, o que mais me deixa curioso é a melhor forma de identificar o que está causando esses problemas de desempenho.

Procurei janelar a saída, mas não é minha primeira escolha e tenho certeza de que estou fazendo algo errado, em vez de a causa ser muitos elementos renderizados.

Eu aprecio seu tempo e agradeço antecipadamente por qualquer dica que você possa fornecer!

Respostas

2 HarrisonGrieve Aug 16 2020 at 15:50

Parece que respondi minha própria pergunta. O problema era um problema de reconciliação devido ao uso de UUID como um suporte chave em meus componentes filhos, o que os fazia renderizar novamente toda vez que o estado de minimização era alterado. Dos documentos:

As chaves devem ser estáveis, previsíveis e exclusivas. Chaves instáveis ​​(como as produzidas por Math.random()) farão com que muitas instâncias de componentes e nós DOM sejam recriados desnecessariamente, o que pode causar degradação do desempenho e perda de estado em componentes filhos.

Vou deixar as etapas aqui para qualquer outra pessoa que se deparar com esse problema.


Depois de pesquisar (muito tempo) no criador de perfil de desempenho, notei que cada vez que minimizava ou expandia os elementos, cada filho estava sendo montado novamente. Depois de consultar o Google com uma consulta mais específica, encontrei esta postagem no blog e percebi que estava cometendo esse flagrante erro de desempenho.

Depois de encontrar a origem do problema, encontrei muitas outras referências a ele.

Depois de corrigir o suporte principal, o tempo de interação ficou ~ 60% mais rápido para minimizar/expandir tudo.

Por fim, memorizei alguns outros componentes relacionados ao filtro instantâneo e , finalmente , parece estar funcionando tão bem quanto eu gostaria por enquanto.

Obrigado a todos que deram uma olhada nisso enquanto isso, e espero que seja útil para qualquer um que possa se deparar com isso.