why-did-you-render
由 Welldone Software 猴子補丁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 的預設環境是「development」。如果您在使用react-native時不使用expo,以下方法將對您有所幫助。
module.exports = { 預設:['模組:metro-react-native-babel-preset'],env:{ 開發:{ 插件:[['@babel/plugin-transform-react-jsx',{ 運行時:'經典的' }]], }, },}
您可以透過babel-preset-expo
將參數傳遞給@babel/preset-react
// babel.config.jsmodule.exports = function (api) { api.cache(true); 回傳 { 預設:[ [ "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 === 'development') { const WhyDidYouRender = require('@welldone-software/why-did-you-render'); WhyDidYouRender(React, { trackAllPureComponents: true, });}
注意:該庫不應該在生產中使用,因為它會減慢 React 的速度
在 Typescript 中,呼叫檔案 wdyr.ts 並將以下行新增至檔案頂部以匯入套件的類型:
/// <reference types="@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';// 對於react-native,您可能需要使用 // __DEV__ 標誌而不是 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'] ] });}
請注意,目前在
webpack
中重寫導入文件的匯出有問題。一個快速的解決方法可以幫助解決這個問題:#85 - trackExtraHooks 無法設定屬性。
為什麼要渲染 Mr. Big Pure React 元件?
該庫可以幫助解決常見的修復場景
React Hooks - 了解並修復 Hooks 問題
為什麼發布了 Render v4! - TypeScript 支援、自訂鉤子追蹤(如 React-Redux 的 useSelector)、追蹤所有純元件。
Next.js 範例
附 Hook 的 React-Redux
目前不支援 Mobx
由 @allen-hsu 製作的 React-Native Flipper 插件
您可以在官方沙箱中測試該庫。
另一個帶有鉤子跟踪的官方沙箱
您可以使用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: '選單'}
logOnDifferentValues
:
通常,只有由 props / state 觸發通知中的相同值引起的重新渲染:
渲染(<Menu a={1}/>)渲染(<Menu a={1}/>)
即使由於不同的道具/狀態而發生通知,此選項也會觸發通知(因此,由於“合法”重新渲染):
渲染(<Menu a={1}/>)渲染(<Menu a={2}/>)
customName
:
有時組件的名稱可能會遺失或非常不方便。例如:
withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(lifecycle)(withPropsOnChange(withPropsOnChange(onlyUpdateFor(life))(LoadNamespace(Connect(withStateState())withpswith())問題上())()))問題上())問題上) ))))))))))))))))))))
您可以選擇傳入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
選項按元件的顯示名稱包含或排除對元件的追蹤。
例如,以下程式碼用於追蹤較舊的 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
)解決重新渲染問題的一種方法是阻止元件擁有者重新渲染。
此選項預設為true
,它允許您查看擁有者元件重新渲染的原因。
false
)通常,您只需要有關元件重新渲染的日誌(當元件重新渲染可以避免時)。
使用此選項,可以追蹤所有重新渲染。
例如:
render(<BigListPureComponent a={1}/>)render(<BigListPureComponent a={2}/>)// 僅當您使用 {logOnDifferentValues: true} 時才會記錄
500
)偵測到熱重載後忽略更新的時間(以毫秒為單位)。
當偵測到熱重載時,我們會忽略hotReloadBufferMs
的所有更新,以免傳送垃圾郵件到控制台。
false
)如果您不想使用console.group
對日誌進行分組,您可以將它們列印為簡單日誌。
false
)分組的日誌可以折疊。
'#058'
)'blue'
)'red'
)控制控制台通知中使用的顏色
如果預設通知程式無法滿足您的需求,您可以建立自訂通知程式。
undefined
)您可以提供一個從原始反應元素中獲取附加資料的函數。從此函數傳回的物件將新增至 OwnerDataMap 中,稍後可以在通知程式函數重寫中存取該物件。
如果您在生產環境中,WDYR 可能會被停用。
可能沒有跟踪任何組件
再次查看追蹤組件。
如果您只使用trackAllPureComponents: true
追蹤純元件,那麼您將只追蹤(React.PureComponent 或 React.memo),也許您的元件都不是純元件,因此它們都不會被追蹤。
也許你沒有問題
嘗試透過在入口點臨時渲染整個應用程式兩次來引發問題:
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 組件。
要解決此問題,請在連接後將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
若要查看庫的來源映射,請使用來源映射載入器。
受到以下先前工作的啟發:
github.com/maicki/why-did-you-update(不再公開),我有機會維護它一段時間。
https://github.com/garbles/why-did-you-update 這個想法歸功於對 React 效能除錯的深入研究。
該庫已獲得麻省理工學院許可。