why-did-you-render
por Welldone Software monkey patches React
para notificá-lo sobre re-renderizações potencialmente evitáveis. (Funciona também com React Native
.)
Por exemplo, se você passar style={{width: '100%'}}
para um grande componente puro, ele sempre será renderizado novamente a cada criação de elemento:
<Estilo BigListPureComponent={{largura: '100%'}}/>
Também pode ajudá-lo a rastrear simplesmente quando e por que um determinado componente é renderizado novamente.
A última versão da biblioteca foi testada (testes unitários e E2E) apenas com React@18
. Para React 17 e 16, use a versão @^7.
npm install @welldone-software/why-did-you-render --save-dev
ou
yarn add --dev @welldone-software/why-did-you-render
Se você usar a transformação JSX automatic
, defina a biblioteca como a fonte de importação e certifique-se de que preset-react
esteja no modo de development
.
['@babel/preset-react', { tempo de execução: 'automático', desenvolvimento: process.env.NODE_ENV === 'desenvolvimento', importSource: '@welldone-software/why-did-you-render',}]
Infelizmente, o metro-react-native-babel-preset
que vem com o react-native pronto para uso não permite que você altere as opções do plugin babel/plugin-transform-react-jsx
. Basta adicionar o plugin com as opções listadas abaixo e iniciar o empacotador react-native normalmente. O ambiente padrão para babel é "desenvolvimento". Se você não usa expo ao trabalhar com react-native, o método a seguir irá ajudá-lo.
module.exports = {predefinições: ['module:metro-react-native-babel-preset'], env: {desenvolvimento: {plugins: [['@babel/plugin-transform-react-jsx', { tempo de execução: ' clássico' }]], }, },}
Você pode passar parâmetros para @babel/preset-react
através de babel-preset-expo
// babel.config.jsmodule.exports = function (api) { api.cache(true); return { predefinições: [ [ "babel-preset-expo", { jsxImportSource: "@welldone-software/por que-você-renderizou", }, ], ], };};
Aviso: Create React App (CRA) ^4 usa a transformação JSX
automatic
. Veja o comentário a seguir sobre como fazer esta etapa com CRA
Crie um arquivo wdyr.js
e importe-o como a primeira importação em seu aplicativo.
wdyr.js
:
importar React de 'react';if (process.env.NODE_ENV === 'desenvolvimento') { const WhyDidYouRender = require('@welldone-software/why-did-you-render'); WhyDidYouRender(React, { trackAllPureComponents: true, });}
Aviso: A biblioteca NUNCA deve ser usada em produção porque torna o React lento
No Typescript, chame o arquivo wdyr.ts e adicione a seguinte linha ao topo do arquivo para importar os tipos do pacote:
/// <tipos de referência="@welldone-software/why-did-you-render" />
Importe wdyr
como a primeira importação (mesmo antes de react-hot-loader
):
index.js
:
importar './wdyr'; // <--- first importimport 'react-hot-loader';importar {hot} de 'react-hot-loader/root';importar React de 'react';importar ReactDOM de 'react-dom';// . ..importar {App} de './app';// ...const HotApp = hot(App);// ...ReactDOM.render(<HotApp/>, document.getElementById('root'));
Se você usar trackAllPureComponents
como sugerimos, todos os componentes puros (React.PureComponent ou React.memo) serão rastreados.
Caso contrário, adicione whyDidYouRender = true
às classes/funções de componentes que você deseja rastrear. (fe Component.whyDidYouRender = true
)
Mais informações sobre o que é rastreado podem ser encontradas em Componentes de Rastreamento.
Não consegue ver nenhum registro WDYR? Confira a seção de solução de problemas ou pesquise nos problemas.
Além disso, o rastreamento de ganchos personalizados é possível usando trackExtraHooks
. Por exemplo, se você deseja rastrear useSelector
do React Redux:
wdyr.js
:
import React from 'react';// Para react-native você pode querer usar // o sinalizador __DEV__ em vez de process.env.NODE_ENV === 'development'if (process.env.NODE_ENV === 'development') { const WhyDidYouRender = require('@welldone-software/why-did-you-render'); const ReactRedux = require('react-redux'); WhyDidYouRender(React, { trackAllPureComponents: true, trackExtraHooks: [ [ReactRedux, 'useSelector'] ] });}
Observe que atualmente há um problema ao reescrever as exportações de arquivos importados em
webpack
. Uma solução rápida pode ajudar: # 85 - trackExtraHooks não pode definir propriedades.
Por que você renderizou o componente Mr. Big Pure React ???
Cenários de correção comuns nos quais esta biblioteca pode ajudar
React Hooks – Entenda e corrija problemas de hooks
Por que você lançou a versão 4! - Suporte TypeScript, rastreamento de ganchos personalizados (como useSelector do React-Redux), rastreamento de todos os componentes puros.
Exemplo Next.js
React-Redux com ganchos
Mobx atualmente não é compatível
Plug-in React-Native flipper feito por @allen-hsu
Você pode testar a biblioteca no sandbox oficial.
E outro sandbox oficial com rastreamento de ganchos
Você pode rastrear todos os componentes puros (React.PureComponent ou React.memo) usando a opção trackAllPureComponents: true
.
Você também pode rastrear manualmente qualquer componente que desejar, definindo whyDidYouRender
neles assim:
class BigList estende React.Component { static WhyDidYouRender = true render(){ return ( //alguma renderização pesada que você deseja garantir que não aconteça se não for necessário) }}
Ou para componentes funcionais:
const BigListPureComponent = props => ( <div> //algum componente pesado que você deseja garantir que não aconteça se não for necessário </div>)BigListPureComponent.whyDidYouRender = true
Você também pode passar um objeto para especificar configurações de rastreamento mais avançadas:
EnhancedMenu.whyDidYouRender = {logOnDifferentValues: true, customName: 'Menu'}
logOnDifferentValues
:
Normalmente, apenas re-renderizações causadas por valores iguais em notificações de gatilho de props/estado:
render(<Menu a={1}/>)render(<Menu a={1}/>)
Esta opção irá acionar notificações mesmo que elas tenham ocorrido devido a diferentes adereços/estados (portanto, devido a re-renderizações "legítimas"):
render(<Menu a={1}/>)render(<Menu a={2}/>)
customName
:
Às vezes, o nome do componente pode estar faltando ou ser muito inconveniente. Por exemplo:
withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(ciclo de vida(withPropsOnChange(withPropsOnChange(onlyUpdateForKeys(LoadNamesp ace(Connect(withState(withState(withPropsOnChange(lifecycle(withPropsOnChange(withHandlers(withHandlers(withHandlers(withHandlers(Connect(lifecycle(Menu))))))))))))))))))))))))
Opcionalmente, você pode passar options
como segundo parâmetro. As seguintes opções estão disponíveis:
include: [RegExp, ...]
( null
por padrão)
exclude: [RegExp, ...]
( null
por padrão)
trackAllPureComponents: false
trackHooks: true
trackExtraHooks: []
logOwnerReasons: true
logOnDifferentValues: false
hotReloadBufferMs: 500
onlyLogs: false
collapseGroups: false
titleColor
diffNameColor
diffPathColor
notifier: ({Component, displayName, hookName, prevProps, prevState, prevHook, nextProps, nextState, nextHook, reason, options, ownerDataMap}) => void
getAdditionalOwnerData: (element) => {...}
null
) Você pode incluir ou excluir o rastreamento de componentes por seu nome usando as opções include
e exclude
.
Por exemplo, o código a seguir é usado para rastrear todas as re-renderizações redundantes causadas por React-Redux mais antigos:
porquêDidYouRender(React, {inclui: [/^ConnectFunction/] });
Aviso: excluir tem prioridade sobre
include
e definir manualmentewhyDidYouRender =
false
) Você pode rastrear todos os componentes puros (componentes React.memo
e React.PureComponent
)
Aviso: você pode excluir o rastreamento de qualquer componente específico com
whyDidYouRender = false
true
)Você pode desativar o rastreamento de alterações de ganchos.
Entenda e corrija problemas de gancho.
[]
)Rastreie ganchos personalizados:
WhyDidYouRender(React, { trackExtraHooks: [ // observe que 'useSelector' é uma exportação nomeada [ReactRedux, 'useSelector'], ]});
Atualmente há um problema ao reescrever as exportações de arquivos importados no webpack. Uma solução alternativa está disponível aqui: #85 - trackExtraHooks não pode definir a propriedade
true
)Uma maneira de corrigir problemas de nova renderização é impedir que o proprietário do componente faça uma nova renderização.
Esta opção é true
por padrão e permite visualizar os motivos pelos quais um componente proprietário é renderizado novamente.
false
)Normalmente, você só deseja logs sobre re-renderizações de componentes quando elas poderiam ter sido evitadas.
Com esta opção, é possível rastrear todas as re-renderizações.
Por exemplo:
render(<BigListPureComponent a={1}/>)render(<BigListPureComponent a={2}/>)// só registrará se você usar {logOnDifferentValues: true}
500
)Tempo em milissegundos para ignorar atualizações após a detecção de uma recarga a quente.
Quando um hot reload é detectado, ignoramos todas as atualizações de hotReloadBufferMs
para não enviar spam para o console.
false
) Se você não quiser usar console.group
para agrupar logs, você pode imprimi-los como logs simples.
false
)Os logs agrupados podem ser recolhidos.
'#058'
)'blue'
)'red'
)Controla as cores usadas nas notificações do console
Você pode criar um notificador personalizado se o padrão não atender às suas necessidades.
undefined
)Você pode fornecer uma função que coleta dados adicionais do elemento react original. O objeto retornado desta função será adicionado ao proprietárioDataMap que pode ser acessado posteriormente na substituição da função do notificador.
Se você estiver em produção, o WDYR provavelmente estará desativado.
Talvez nenhum componente seja rastreado
Confira os componentes de rastreamento mais uma vez.
Se você rastrear apenas componentes puros usando trackAllPureComponents: true
então você rastrearia apenas (React.PureComponent ou React.memo), talvez nenhum de seus componentes seja puro, então nenhum deles será rastreado.
Talvez você não tenha problemas
Tente causar um problema renderizando temporariamente o aplicativo inteiro duas vezes no ponto de entrada:
index.js
:
const HotApp = hot(App);HotApp.whyDidYouRender = true;ReactDOM.render(<HotApp/>, document.getElementById('root'));ReactDOM.render(<HotApp/>, document.getElementById('root') );
Atualmente há um problema ao reescrever as exportações de arquivos importados em webpack
. Uma solução rápida pode ajudar: # 85 - trackExtraHooks não pode definir propriedades.
connect
HOC está enviando spam para o console Como a estática das talhas connect
, se você adicionar WDYR ao componente interno, ele também será adicionado ao componente HOC onde ganchos complexos estão em execução.
Para corrigir isso, adicione o whyDidYouRender = true
estático a um componente após a conexão:
const SimpleComponent = ({a}) => <div data-testid="foo">{ab}</div>) // não antes da conexão: // SimpleComponent.whyDidYouRender = true const ConnectedSimpleComponent = connect( state => ({a: state.a}) )(SimpleComponent) // após a conexão: SimpleComponent.whyDidYouRender = true
Para ver os mapas de origem da biblioteca, use o source-map-loader.
Inspirado no seguinte trabalho anterior:
github.com/maicki/why-did-you-update (não é mais público) que tive a oportunidade de manter por algum tempo.
https://github.com/garbles/why-did-you-update onde Um mergulho profundo na depuração de desempenho do React é creditado pela ideia.
Esta biblioteca é licenciada pelo MIT.