why-did-you-render
von Welldone Software Monkey Patches React
, um Sie über potenziell vermeidbare erneute Renderings zu benachrichtigen. (Funktioniert auch mit React Native
.)
Wenn Sie beispielsweise style={{width: '100%'}}
an eine große reine Komponente übergeben, wird diese bei jeder Elementerstellung immer neu gerendert:
<BigListPureComponent style={{width: '100%'}}/>
Es kann Ihnen auch dabei helfen, einfach zu verfolgen, wann und warum eine bestimmte Komponente erneut gerendert wird.
Die neueste Version der Bibliothek wurde nur mit React@18
getestet (Unit-Tests und E2E). Für React 17 und 16 verwenden Sie bitte Version @^7.
npm install @welldone-software/why-did-you-render --save-dev
oder
yarn add --dev @welldone-software/why-did-you-render
Wenn Sie die automatic
JSX-Transformation verwenden, legen Sie die Bibliothek als Importquelle fest und stellen Sie sicher, dass sich preset-react
im development
befindet.
['@babel/preset-react', { runtime: 'automatic', development: process.env.NODE_ENV === 'development', importSource: '@welldone-software/why-did-you-render',}]
Leider können Sie mit dem metro-react-native-babel-preset
, das standardmäßig mit React-Native geliefert wird, die Optionen des Plugins babel/plugin-transform-react-jsx
nicht ändern. Fügen Sie einfach das Plugin mit den unten aufgeführten Optionen hinzu und starten Sie den React-Native Packager wie gewohnt. Die Standardumgebung für babel ist „development“. Wenn Sie bei der Arbeit mit React-Native kein Expo verwenden, hilft Ihnen die folgende Methode.
module.exports = { Presets: ['module:metro-react-native-babel-preset'], env: { Entwicklung: { Plugins: [['@babel/plugin-transform-react-jsx', { Laufzeit: ' klassisch' }]], }, },}
Sie können Parameter über babel-preset-expo
an @babel/preset-react
übergeben
// babel.config.jsmodule.exports = function (api) { api.cache(true); return { Presets: [ [ "babel-preset-expo", { jsxImportSource: "@welldone-software/why-did-you-render", }, ], ], };};
Hinweis: Create React App (CRA) ^4 verwendet die
automatic
JSX-Transformation. Im folgenden Kommentar erfahren Sie, wie Sie diesen Schritt mit CRA durchführen
Erstellen Sie eine wdyr.js
Datei und importieren Sie sie als ersten Import in Ihre Anwendung.
wdyr.js
:
import React from 'react';if (process.env.NODE_ENV === 'development') { const whyDidYouRender = require('@welldone-software/why-did-you-render'); whyDidYouRender(React, { trackAllPureComponents: true, });}
Hinweis: Die Bibliothek sollte NIEMALS in der Produktion verwendet werden, da sie React verlangsamt
Rufen Sie in Typescript die Datei wdyr.ts auf und fügen Sie die folgende Zeile am Anfang der Datei hinzu, um die Pakettypen zu importieren:
/// <referencetypes="@welldone-software/why-did-you-render" />
Importieren Sie wdyr
als ersten Import (noch vor react-hot-loader
):
index.js
:
import './wdyr'; // <--- first importimport 'react-hot-loader';import {hot} from 'react-hot-loader/root';import React from 'react';import ReactDOM from 'react-dom';// . ..import {App} from './app';// ...const HotApp = hot(App);// ...ReactDOM.render(<HotApp/>, document.getElementById('root'));
Wenn Sie trackAllPureComponents
wie von uns vorgeschlagen verwenden, werden alle reinen Komponenten (React.PureComponent oder React.memo) verfolgt.
Andernfalls fügen Sie whyDidYouRender = true
zu den Komponentenklassen/Funktionen hinzu, die Sie verfolgen möchten. (z. B. Component.whyDidYouRender = true
)
Weitere Informationen darüber, was verfolgt wird, finden Sie unter Tracking-Komponenten.
Sie können keine WDYR-Protokolle sehen? Sehen Sie sich den Abschnitt zur Fehlerbehebung an oder suchen Sie in den Problemen.
Außerdem ist die Verfolgung benutzerdefinierter Hooks mithilfe von trackExtraHooks
möglich. Wenn Sie beispielsweise useSelector
von React Redux verfolgen möchten:
wdyr.js
:
import React from 'react';// Für React-Native möchten Sie möglicherweise // das __DEV__-Flag anstelle von process.env.NODE_ENV === 'development'if (process.env.NODE_ENV === 'development') verwenden. { const whyDidYouRender = require('@welldone-software/why-did-you-render'); const ReactRedux = require('react-redux'); whyDidYouRender(React, { trackAllPureComponents: true, trackExtraHooks: [ [ReactRedux, 'useSelector'] ] });}
Beachten Sie, dass es derzeit ein Problem beim Umschreiben von Exporten importierter Dateien im
webpack
gibt. Ein schneller Workaround kann Abhilfe schaffen: #85 – trackExtraHooks kann keine Eigenschaft festlegen.
Warum haben Sie die Mr. Big Pure React-Komponente gerendert???
Häufige Fehlerbehebungsszenarien, bei denen diese Bibliothek helfen kann
Reagieren Sie auf Hooks – Hook-Probleme verstehen und beheben
Warum haben Sie Render v4 veröffentlicht? - TypeScript-Unterstützung, Nachverfolgung benutzerdefinierter Hooks (wie useSelector von React-Redux), Nachverfolgung aller reinen Komponenten.
Next.js-Beispiel
React-Redux mit Hooks
Mobx wird derzeit nicht unterstützt
React-Native Flipper-Plugin von @allen-hsu
Sie können die Bibliothek in der offiziellen Sandbox testen.
Und eine weitere offizielle Sandbox mit Hooks-Tracking
Sie können alle reinen Komponenten (React.PureComponent oder React.memo) mit der Option trackAllPureComponents: true
verfolgen.
Sie können jede gewünschte Komponente auch manuell verfolgen, indem Sie whyDidYouRender
wie folgt darauf festlegen:
class BigList erweitert React.Component { static whyDidYouRender = true render(){ return ( //ein umfangreiches Rendering, das Sie sicherstellen möchten, dass es nicht passiert, wenn es nicht notwendig ist) }}
Oder für funktionale Komponenten:
const BigListPureComponent = props => ( <div> //eine schwere Komponente, von der Sie sicherstellen möchten, dass sie nicht auftritt, wenn sie nicht erforderlich ist </div>)BigListPureComponent.whyDidYouRender = true
Sie können auch ein Objekt übergeben, um erweiterte Tracking-Einstellungen festzulegen:
EnhancedMenu.whyDidYouRender = { logOnDifferentValues: true, customName: 'Menu'}
logOnDifferentValues
:
Normalerweise lösen nur erneute Renderings, die durch gleiche Werte in Requisiten/Status verursacht werden, Benachrichtigungen aus:
render(<Menu a={1}/>)render(<Menu a={1}/>)
Diese Option löst Benachrichtigungen auch dann aus, wenn sie aufgrund unterschiedlicher Requisiten/Status auftreten (also aufgrund „legitimer“ erneuter Renderings):
render(<Menu a={1}/>)render(<Menu a={2}/>)
customName
:
Manchmal kann der Name der Komponente fehlen oder sehr unpraktisch sein. Zum Beispiel:
withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(lifecycle(withPropsOnChange(withPropsOnChange(onlyUpdateForKeys(LoadNamesp ace(Connect(withState(withState(withPropsOnChange(lifecycle(withPropsOnChange(withHandlers(withHandlers(withHandlers(withHandlers(Connect(lifecycle(Menu))))))))))))))))))))))
Optional können Sie options
als zweiten Parameter übergeben. Folgende Optionen stehen zur Verfügung:
include: [RegExp, ...]
(standardmäßig null
)
exclude: [RegExp, ...]
(standardmäßig null
)
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
) Mithilfe der include
und exclude
können Sie die Verfolgung von Komponenten anhand ihres Anzeigenamens ein- oder ausschließen.
Der folgende Code wird beispielsweise verwendet, um alle redundanten Neu-Renderings zu verfolgen, die durch älteres React-Redux verursacht werden:
whyDidYouRender(React, { include: [/^ConnectFunction/] });
Hinweis: Ausschließen hat Vorrang vor
include
und dem manuellen FestlegenwhyDidYouRender =
false
) Sie können alle reinen Komponenten verfolgen (sowohl React.memo
als auch React.PureComponent
-Komponenten).
Hinweis: Sie können das Tracking einer bestimmten Komponente mit
whyDidYouRender = false
ausschließen
true
)Sie können die Verfolgung von Hook-Änderungen deaktivieren.
Hook-Probleme verstehen und beheben.
[]
)Verfolgen Sie benutzerdefinierte Hooks:
whyDidYouRender(React, { trackExtraHooks: [ // Beachten Sie, dass „useSelector“ ein benannter Export ist [ReactRedux, „useSelector“], ]});
Derzeit gibt es ein Problem beim Umschreiben von Exporten importierter Dateien im Webpack. Eine Problemumgehung ist hier verfügbar: #85 – trackExtraHooks kann die Eigenschaft nicht festlegen
true
)Eine Möglichkeit, Probleme beim erneuten Rendern zu beheben, besteht darin, den Eigentümer der Komponente am erneuten Rendern zu hindern.
Diese Option ist standardmäßig true
und ermöglicht Ihnen, die Gründe anzuzeigen, warum eine Eigentümerkomponente erneut gerendert wird.
false
)Normalerweise möchten Sie nur Protokolle über das erneute Rendern von Komponenten, wenn diese hätten vermieden werden können.
Mit dieser Option ist es möglich, alle erneuten Renderings zu verfolgen.
Zum Beispiel:
render(<BigListPureComponent a={1}/>)render(<BigListPureComponent a={2}/>)// protokolliert nur, wenn Sie {logOnDifferentValues: true} verwenden
500
)Zeit in Millisekunden, um Aktualisierungen zu ignorieren, nachdem ein Hot-Reload erkannt wurde.
Wenn ein Hot-Reload erkannt wird, ignorieren wir alle Updates für hotReloadBufferMs
um die Konsole nicht zu spammen.
false
) Wenn Sie console.group
nicht zum Gruppieren von Protokollen verwenden möchten, können Sie diese als einfache Protokolle drucken.
false
)Gruppierte Protokolle können reduziert werden.
'#058'
)'blue'
)'red'
)Steuert die Farben, die in den Konsolenbenachrichtigungen verwendet werden
Sie können einen benutzerdefinierten Notifier erstellen, wenn der Standard-Notifier nicht Ihren Anforderungen entspricht.
undefined
)Sie können eine Funktion bereitstellen, die zusätzliche Daten aus dem ursprünglichen Reaktionselement sammelt. Das von dieser Funktion zurückgegebene Objekt wird der OwnerDataMap hinzugefügt, auf die später im Rahmen Ihrer Notifier-Funktionsüberschreibung zugegriffen werden kann.
Wenn Sie in der Produktion arbeiten, ist WDYR wahrscheinlich deaktiviert.
Möglicherweise wird keine Komponente verfolgt
Schauen Sie sich noch einmal die Tracking-Komponenten an.
Wenn Sie nur reine Komponenten mit trackAllPureComponents: true
verfolgen, würden Sie nur entweder (React.PureComponent oder React.memo) verfolgen, möglicherweise ist keine Ihrer Komponenten rein, sodass keine von ihnen verfolgt wird.
Vielleicht hast du keine Probleme
Versuchen Sie, ein Problem zu verursachen, indem Sie die gesamte App am Einstiegspunkt vorübergehend zweimal rendern:
index.js
:
const HotApp = hot(App);HotApp.whyDidYouRender = true;ReactDOM.render(<HotApp/>, document.getElementById('root'));ReactDOM.render(<HotApp/>, document.getElementById('root') );
Derzeit gibt es ein Problem beim Umschreiben von Exporten importierter Dateien im webpack
. Ein schneller Workaround kann Abhilfe schaffen: #85 – trackExtraHooks kann keine Eigenschaft festlegen.
connect
HOC spammt die Konsole Wenn Sie WDYR zur inneren Komponente hinzufügen, wird es aufgrund connect
Hebezeug-Statik auch zur HOC-Komponente hinzugefügt, in der komplexe Hooks ausgeführt werden.
Um dies zu beheben, fügen Sie einer Komponente nach der Verbindung die statische Variable whyDidYouRender = true
hinzu:
const SimpleComponent = ({a}) => <div data-testid="foo">{ab}</div>) // nicht vor dem connect: // SimpleComponent.whyDidYouRender = true const ConnectedSimpleComponent = connect( state => ({a: state.a}) )(SimpleComponent) // nach der Verbindung: SimpleComponent.whyDidYouRender = true
Um die Quellkarten der Bibliothek anzuzeigen, verwenden Sie den Source-Map-Loader.
Inspiriert durch die folgende frühere Arbeit:
github.com/maicki/why-did-you-update (nicht mehr öffentlich), das ich einige Zeit pflegen durfte.
https://github.com/garbles/why-did-you-update, wo die Idee auf einen tiefen Einblick in das React-Perf-Debugging zurückzuführen ist.
Diese Bibliothek ist MIT-lizenziert.