Welldone Software によるwhy-did-you-render
のモンキー パッチは、回避可能な可能性のある再レンダリングについて通知するためにReact
。 ( React Native
でも動作します。)
たとえば、 style={{width: '100%'}}
を大きな純粋なコンポーネントに渡すと、要素が作成されるたびに常に再レンダリングされます。
<BigListPureComponent style={{width: '100%'}}/>
また、特定のコンポーネントがいつ、そしてなぜ再レンダリングされるかを簡単に追跡するのにも役立ちます。
ライブラリの最新バージョンは、 React@18
のみでテストされました (単体テストと E2E)。 React 17 および 16 の場合は、バージョン @^7 を使用してください。
npm install @welldone-software/why-did-you-render --save-dev
または
yarn add --dev @welldone-software/why-did-you-render
automatic
JSX 変換を使用する場合は、インポート ソースとなるライブラリを設定し、 preset-react
がdevelopment
モードであることを確認してください。
['@babel/preset-react', { ランタイム: '自動'、開発: process.env.NODE_ENV === '開発'、importSource: '@welldone-software/why-did-you-render',}]
残念ながら、react-native にそのまま付属しているmetro-react-native-babel-preset
babel/plugin-transform-react-jsx
プラグインのオプションを変更できません。以下にリストされているオプションを使用してプラグインを追加し、通常どおり React-native Packager を起動するだけです。 babel のデフォルトの環境は「開発」です。 React-Native を使用するときに expo を使用しない場合は、次の方法が役に立ちます。
module.exports = { presets: ['module:metro-react-native-babel-preset'], env: {development: { plugins: [['@babel/plugin-transform-react-jsx', { runtime: 'クラシック' }]]、 }、 }、}
babel-preset-expo
を通じて@babel/preset-react
にパラメータを渡すことができます
// babel.config.jsmodule.exports = function (api) { api.cache(true); return { プリセット: [ [ "babel-preset-expo", { jsxImportSource: "@welldone-software/why-did-you-render", }, ], ], };};
注意: Create React App (CRA) ^4 は
automatic
JSX 変換を使用します。 CRA でこのステップを実行する方法については、次のコメントを参照してください。
wdyr.js
ファイルを作成し、それをアプリケーションの最初のインポートとしてインポートします。
wdyr.js
:
import React from 'react';if (process.env.NODE_ENV === '開発') { const WhyDidYouRender = require('@welldone-software/why-did-you-render'); WhyDidYouRender(React, { trackAllPureComponents: true, });}
注意: このライブラリは React の速度を低下させるため、本番環境では決して使用しないでください。
Typescript で、ファイル wdyr.ts を呼び出し、ファイルの先頭に次の行を追加して、パッケージのタイプをインポートします。
/// <reference type="@welldone-software/why-did-you-render" />
最初のインポートとしてwdyr
インポートします ( react-hot-loader
前でも)。
index.js
:
'./wdyr' をインポートします。 // <--- 最初の 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'));
私たちが提案するようにtrackAllPureComponents
を使用すると、すべての純粋なコンポーネント (React.PureComponent または React.memo) が追跡されます。
それ以外の場合は、追跡するコンポーネント クラス/関数にwhyDidYouRender = true
を追加します。 (fe Component.whyDidYouRender = true
)
追跡対象の詳細については、「追跡コンポーネント」を参照してください。
WDYR ログが表示されませんか?トラブルシューティング セクションを確認するか、問題を検索してください。
また、 trackExtraHooks
使用することでカスタム フックを追跡することもできます。たとえば、React Redux からuseSelector
追跡する場合:
wdyr.js
:
import React from 'react';// 反応ネイティブの場合は、 // process.env.NODE_ENV === 'development'if (process.env.NODE_ENV === 'development') の代わりに __DEV__ フラグを使用するとよいでしょう。 { const WhyDidYouRender = require('@welldone-software/why-did-you-render'); const ReactRedux = require('react-redux'); WhyDidYouRender(React, { trackAllPureComponents: true, trackExtraHooks: [ [ReactRedux, 'useSelector'] ] });}
現在、
webpack
でインポートされたファイルのエクスポートの書き換えに問題があることに注意してください。これには簡単な回避策が役立ちます: #85 - trackExtraHooks はプロパティを設定できません。
なぜ Mr. Big Pure React コンポーネントをレンダリングしたのですか?
このライブラリが役立つ一般的な修正シナリオ
React Hooks - フックの問題を理解して修正する
なぜレンダリングしたのか v4 がリリースされました! - TypeScript のサポート、カスタム フックの追跡 (React-Redux の useSelector など)、すべての純粋なコンポーネントの追跡。
Next.js の例
フックを使用した React-Redux
Mobx は現在サポートされていません
@allen-hsu が作成した React-Native フリッパー プラグイン
公式サンドボックスでライブラリをテストできます。
そして、フック追跡を備えた別の公式サンドボックス
trackAllPureComponents: true
オプションを使用すると、すべての純粋なコンポーネント (React.PureComponent または React.memo) を追跡できます。
次のようにwhyDidYouRender
コンポーネントに設定することで、必要なコンポーネントを手動で追跡することもできます。
class BigList extends React.Component { static WhyDidYouRender = true render(){ return ( //必要でない場合は確実に発生しないようにしたい重いレンダリング ) }}
または機能コンポーネントの場合:
const BigListPureComponent = props => ( <div> // 必要でない場合は発生しないようにしたい重いコンポーネント </div>)BigListPureComponent.whyDidYouRender = true
オブジェクトを渡して、より高度な追跡設定を指定することもできます。
EnhancedMenu.whyDidYouRender = { logOnDifferentValues: true、customName: 'Menu'}
logOnDifferentValues
:
通常、props/state の値が等しいことによって発生する再レンダリングのみが通知をトリガーします。
render(<Menu a={1}/>)render(<Menu a={1}/>)
このオプションは、プロパティや状態が異なるために通知が発生した場合でも通知をトリガーします (したがって、「正当な」再レンダリングのため)。
render(<Menu a={1}/>)render(<Menu a={2}/>)
customName
:
場合によっては、コンポーネントの名前が欠落していたり、非常に不便な場合があります。例えば:
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))))))))))))))))))
必要に応じて、2 番目のパラメータとしてoptions
を渡すことができます。次のオプションが利用可能です。
include: [RegExp, ...]
(デフォルトではnull
)
exclude: [RegExp, ...]
(デフォルトでは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
) include
およびexclude
オプションを使用すると、displayName によってコンポーネントの追跡を含めたり除外したりできます。
たとえば、次のコードは、古い React-Redux によって引き起こされるすべての冗長な再レンダリングを追跡するために使用されます。
WhyDidYouRender(React, { include: [/^ConnectFunction/] });
注意:除外は、
include
よりも優先され、手動でwhyDidYouRender =
設定することもできます。
false
)すべての純粋なコンポーネント ( React.memo
とReact.PureComponent
コンポーネントの両方) を追跡できます。
注意:
whyDidYouRender = false
を使用すると、特定のコンポーネントの追跡を除外できます。
true
)フックの変更の追跡をオフにすることができます。
フックの問題を理解し、修正します。
[]
)カスタムフックを追跡する:
WhyDidYouRender(React, { trackExtraHooks: [ // 'useSelector' は名前付きエクスポートであることに注意してください [ReactRedux, 'useSelector'], ]});
現在、Webpack でインポートされたファイルのエクスポートの書き換えに問題があります。回避策はここで利用できます: #85 - trackExtraHooks はプロパティを設定できません
true
)再レンダリングの問題を解決する 1 つの方法は、コンポーネントの所有者が再レンダリングできないようにすることです。
このオプションはデフォルトでtrue
で、オーナー コンポーネントが再レンダリングされる理由を表示できます。
false
)通常、コンポーネントの再レンダリングに関するログは、回避できた場合にのみ必要になります。
このオプションを使用すると、すべての再レンダリングを追跡できます。
例えば:
render(<BigListPureComponent a={1}/>)render(<BigListPureComponent a={2}/>)// {logOnDifferentValues: true} を使用した場合にのみログが記録されます
500
)ホット リロードが検出された後に更新を無視する時間 (ミリ秒)。
ホット リロードが検出されると、コンソールにスパムを送信しないように、 hotReloadBufferMs
のすべての更新が無視されます。
false
) console.group
を使用してログをグループ化したくない場合は、単純なログとして出力できます。
false
)グループ化されたログは折りたたむことができます。
'#058'
)'blue'
)'red'
)コンソール通知で使用される色を制御します
デフォルトの通知機能がニーズに合わない場合は、カスタム通知機能を作成できます。
undefined
)元の反応要素から追加のデータを収集する関数を提供できます。この関数から返されたオブジェクトは、notifier 関数のオーバーライド内で後でアクセスできる ownerDataMap に追加されます。
運用環境を使用している場合、WDYR はおそらく無効になっています。
コンポーネントが追跡されていない可能性があります
もう一度「追跡コンポーネント」を確認してください。
trackAllPureComponents: true
使用して純粋なコンポーネントのみを追跡する場合は、どちらか (React.PureComponent または React.memo) のみを追跡することになります。おそらく、コンポーネントがどれも純粋ではないため、いずれも追跡されません。
もしかしたら何も問題がないかもしれません
アプリ全体をエントリ ポイントで 2 回一時的にレンダリングして、問題を引き起こしてみてください。
index.js
:
const HotApp = hot(App);HotApp.whyDidYouRender = true;ReactDOM.render(<HotApp/>, document.getElementById('root'));ReactDOM.render(<HotApp/>, document.getElementById('root') );
現在、 webpack
でインポートされたファイルのエクスポートの書き換えに問題があります。これには簡単な回避策が役立ちます: #85 - trackExtraHooks はプロパティを設定できません。
connect
HOC がコンソールにスパム送信していますconnect
静的ホイストを行うため、WDYR を内部コンポーネントに追加すると、複雑なフックが実行されている HOC コンポーネントにも WDYR が追加されます。
これを修正するには、接続後のコンポーネントにwhyDidYouRender = true
静的を追加します。
const SimpleComponent = ({a}) => <div data-testid="foo">{ab}</div>) // 接続前ではない: // SimpleComponent.whyDidYouRender = true const ConnectedSimpleComponent = connect( state => ({a: state.a}) )(SimpleComponent) // 接続後: SimpleComponent.whyDidYouRender = true
ライブラリのソースマップを表示するには、source-map-loader を使用します。
以下の以前の作品からインスピレーションを受けています。
github.com/maicki/why-did-you-update (現在は公開されていません) をしばらく維持する機会がありました。
https://github.com/garbles/why-did-you-update React perf デバッグの詳細がこのアイデアの功績として認められています。
このライブラリは MIT ライセンスを取得しています。