Gathering detailed insights and metrics for @welldone-software/why-did-you-render
Gathering detailed insights and metrics for @welldone-software/why-did-you-render
why-did-you-render by Welldone Software monkey patches React to notify you about potentially avoidable re-renders. (Works with React Native as well.)
npm install @welldone-software/why-did-you-render
Typescript
Module System
Node Version
NPM Version
97.4
Supply Chain
99.6
Quality
90
Maintenance
100
Vulnerability
99.6
License
JavaScript (97.64%)
TypeScript (2.33%)
Shell (0.03%)
Total Downloads
78,419,715
Last Day
77,758
Last Week
470,427
Last Month
1,520,573
Last Year
20,372,744
11,528 Stars
489 Commits
201 Forks
41 Watching
8 Branches
33 Contributors
Minified
Minified + Gzipped
Latest Version
10.0.1
Package Id
@welldone-software/why-did-you-render@10.0.1
Unpacked Size
332.05 kB
Size
46.52 kB
File Count
10
NPM Version
10.9.2
Node Version
23.5.0
Publised On
20 Jan 2025
Cumulative downloads
Total Downloads
Last day
-13.4%
77,758
Compared to previous day
Last week
6.5%
470,427
Compared to previous week
Last month
-12.2%
1,520,573
Compared to previous month
Last year
-0.4%
20,372,744
Compared to previous year
1
1
54
why-did-you-render
by Welldone Software monkey patches React
to notify you about potentially avoidable re-renders. (Works with React Native
as well.)
For example, if you pass style={{width: '100%'}}
to a big memo component it would always re-render on every element creation:
1<MemoBigList style={{width: '100%'}}/>
It can also help you to simply track when and why a certain component re-renders.
[!CAUTION] The library was not tested with React Compiler at all. I believe it's completely incompatible with it.
[!CAUTION] Not all re-renders are "bad". Sometimes shenanigan to reduce re-renders can either hurt your App's performance or have a neglagable effect, in which case it would be just a waste of your efforts, and complicate your code. Try to focus on heavier components when optimizing and use the React profiler inside the React dev-tools to measure the effects of any changes.
[!NOTE] I've joined the React team, specifically working on React tooling. This role has opened up exciting opportunities to enhance the developer experience for React users— and your input could offer valuable insights to help me with this effort. Please join the conversation in the discussion thread!
The latest version of the library was tested (unit tests and E2E) with React@19
only.
React 18
, please see the readme for version @^8.React 17
and React 16
, please see the readme for version @^7.npm install @welldone-software/why-did-you-render --save-dev
or
yarn add @welldone-software/why-did-you-render -D
Set the library to be the React's importSource and make sure preset-react
is in development
mode.
This is because React 19
requires using the automatic
JSX transformation.
1['@babel/preset-react', { 2 runtime: 'automatic', 3 development: process.env.NODE_ENV === 'development', 4 importSource: '@welldone-software/why-did-you-render', 5}]
Add the plugin as listed below and start react-native packager as usual. Default env for babel is "development". If you do not use expo when working with react-native, the following method will help you.
1module.exports = { 2 presets: ['module:metro-react-native-babel-preset'], 3 4 env: { 5 development: { 6 plugins: [['@babel/plugin-transform-react-jsx', { 7 runtime: 'automatic', 8 development: process.env.NODE_ENV === 'development', 9 importSource: '@welldone-software/why-did-you-render', 10 }]], 11 }, 12 }, 13}
You can pass params to @babel/preset-react
through babel-preset-expo
1// babel.config.js 2module.exports = function (api) { 3 api.cache(true); 4 return { 5 presets: [ 6 [ 7 "babel-preset-expo", 8 { 9 jsxImportSource: "@welldone-software/why-did-you-render", 10 }, 11 ], 12 ], 13 }; 14};
Notice: Create React App (CRA) ^4 uses the
automatic
JSX transformation. See the following comment on how to do this step with CRA
Create a wdyr.js
file and import it as the very first import in your application.
wdyr.js
:
1import React from 'react'; 2 3if (process.env.NODE_ENV === 'development') { 4 const whyDidYouRender = require('@welldone-software/why-did-you-render'); 5 whyDidYouRender(React, { 6 trackAllPureComponents: true, 7 }); 8}
[!CAUTION] The library should NEVER be used in production because:
- It significantly slows down React
- It monkey patches React and can result in unexpected behavior
In Typescript, call the file wdyr.ts and add the following line to the top of the file to import the package's types:
1/// <reference types="@welldone-software/why-did-you-render" />
Import wdyr
as the first import (even before react-hot-loader
if you use it):
index.js
:
1import './wdyr'; // <--- first import 2 3import 'react-hot-loader'; 4 5import React from 'react'; 6import ReactDOM from 'react-dom'; 7// ... 8import {App} from './app'; 9// ... 10ReactDOM.render(<App/>, document.getElementById('root'));
If you use trackAllPureComponents
, all pure components (React.PureComponent or React.memo) will be tracked.
Otherwise, add whyDidYouRender = true
to ad-hoc components to track them. (f.e Component.whyDidYouRender = true
)
More information about what is tracked can be found in Tracking Components.
Can't see any WDYR logs? Check out the troubleshooting section or search in the issues.
Also, tracking custom hooks is possible by using trackExtraHooks
. For example if you want to track useSelector
from React Redux:
wdyr.js
:
1import React from 'react'; 2 3// For react-native you might want to use 4// the __DEV__ flag instead of process.env.NODE_ENV === 'development' 5if (process.env.NODE_ENV === 'development') { 6 const whyDidYouRender = require('@welldone-software/why-did-you-render'); 7 const ReactRedux = require('react-redux'); 8 whyDidYouRender(React, { 9 trackAllPureComponents: true, 10 trackExtraHooks: [ 11 [ReactRedux, 'useSelector'] 12 ] 13 }); 14}
Notice that there's currently a problem with rewriting exports of imported files in
webpack
. A quick workaround can help with it: #85 - trackExtraHooks cannot set property.
You can test the library in the official sandbox.
And another official sandbox with hooks tracking
You can track all pure components (React.PureComponent or React.memo) using the trackAllPureComponents: true
option.
You can also manually track any component you want by setting whyDidYouRender
on them like this:
1class BigList extends React.Component { 2 static whyDidYouRender = true 3 render(){ 4 return ( 5 //some heavy render you want to ensure doesn't happen if its not necessary 6 ) 7 } 8}
Or for functional components:
1const BigListPureComponent = props => ( 2 <div> 3 //some heavy component you want to ensure doesn't happen if its not necessary 4 </div> 5) 6BigListPureComponent.whyDidYouRender = true
You can also pass an object to specify more advanced tracking settings:
1EnhancedMenu.whyDidYouRender = { 2 logOnDifferentValues: true, 3 customName: 'Menu' 4}
logOnDifferentValues
:
Normally, only re-renders that are caused by equal values in props / state trigger notifications:
1render(<Menu a={1}/>) 2render(<Menu a={1}/>)
This option will trigger notifications even if they occurred because of different props / state (Thus, because of "legit" re-renders):
1render(<Menu a={1}/>) 2render(<Menu a={2}/>)
customName
:
Sometimes the name of the component can be missing or very inconvenient. For example:
1withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(lifecycle(withPropsOnChange(withPropsOnChange(onlyUpdateForKeys(LoadNamespace(Connect(withState(withState(withPropsOnChange(lifecycle(withPropsOnChange(withHandlers(withHandlers(withHandlers(withHandlers(Connect(lifecycle(Menu)))))))))))))))))))))))
Optionally you can pass in options
as the second parameter. The following options are available:
include: [RegExp, ...]
(null
by default)exclude: [RegExp, ...]
(null
by default)trackAllPureComponents: false
trackHooks: true
trackExtraHooks: []
logOwnerReasons: true
logOnDifferentValues: false
hotReloadBufferMs: 500
onlyLogs: false
collapseGroups: false
titleColor
diffNameColor
diffPathColor
textBackgroundColor
notifier: ({Component, displayName, hookName, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult, reason, options, ownerDataMap}) => void
getAdditionalOwnerData: (element) => {...}
null
)You can include or exclude tracking of components by their displayName using the include
and exclude
options.
For example, the following code is used to track all redundant re-renders that are caused by older React-Redux:
1whyDidYouRender(React, { include: [/^ConnectFunction/] });
Notice: exclude takes priority over both
include
and manually setwhyDidYouRender =
false
)You can track all pure components (both React.memo
and React.PureComponent
components)
Notice: You can exclude the tracking of any specific component with
whyDidYouRender = false
true
)You can turn off tracking of hooks changes.
Understand and fix hook issues.
[]
)Track custom hooks:
1whyDidYouRender(React, { 2 trackExtraHooks: [ 3 // notice that 'useSelector' is a named export 4 [ReactRedux, 'useSelector'], 5 ] 6});
This feature is rewriting exports of imported files. There is currently a problem with that approach in webpack. A workaround is available here: #85 - trackExtraHooks cannot set property
true
)One way of fixing re-render issues is preventing the component's owner from re-rendering.
This option is true
by default and it lets you view the reasons why an owner component re-renders.
false
)Normally, you only want logs about component re-renders when they could have been avoided.
With this option, it is possible to track all re-renders.
For example:
1render(<BigListPureComponent a={1}/>) 2render(<BigListPureComponent a={2}/>) 3// will only log if you use {logOnDifferentValues: true}
500
)Time in milliseconds to ignore updates after a hot reload is detected.
When a hot reload is detected, we ignore all updates for hotReloadBufferMs
to not spam the console.
false
)If you don't want to use console.group
to group logs you can print them as simple logs.
false
)Grouped logs can be collapsed.
'#058'
)'blue'
)'red'
)'white
)Controls the colors used in the console notifications
You can create a custom notifier if the default one does not suite your needs.
undefined
)You can provide a function that harvests additional data from the original react element. The object returned from this function will be added to the ownerDataMap which can be accessed later within your notifier function override.
trackAllPureComponents: true
then you would only track either (React.PureComponent or React.memo), maybe none of your components are pure so none of them will get tracked.Try causing an issue by temporary rendering the whole app twice in it's entry point:
index.js
:
1const HotApp = hot(App);
2HotApp.whyDidYouRender = true;
3ReactDOM.render(<HotApp/>, document.getElementById('root'));
4ReactDOM.render(<HotApp/>, document.getElementById('root'));
There's currently a problem with rewriting exports of imported files in webpack
. A quick workaround can help with it: #85 - trackExtraHooks cannot set property.
connect
HOC is spamming the consoleSince connect
hoists statics, if you add WDYR to the inner component, it is also added to the HOC component where complex hooks are running.
To fix this, add the whyDidYouRender = true
static to a component after the connect:
1 const SimpleComponent = ({a}) => <div data-testid="foo">{a.b}</div>) 2 // not before the connect: 3 // SimpleComponent.whyDidYouRender = true 4 const ConnectedSimpleComponent = connect( 5 state => ({a: state.a}) 6 )(SimpleComponent) 7 // after the connect: 8 SimpleComponent.whyDidYouRender = true
To see the library's sourcemaps use the source-map-loader.
Inspired by the following previous work:
This library is MIT licensed.
No vulnerabilities found.
Reason
30 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
0 existing vulnerabilities detected
Reason
Found 0/7 approved changesets -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-01-13
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More