Security News
ESLint is Now Language-Agnostic: Linting JSON, Markdown, and Beyond
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
@welldone-software/why-did-you-render
Advanced tools
Monkey patches React to notify you about avoidable re-renders.
@welldone-software/why-did-you-render is a tool for debugging unnecessary re-renders in React applications. It helps developers identify and fix performance issues by logging information about why a component re-rendered.
Basic Setup
This code sets up the @welldone-software/why-did-you-render package to track all pure components in a React application. It helps in identifying unnecessary re-renders by logging detailed information.
const React = require('react');
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
trackAllPureComponents: true,
});
Tracking Specific Components
This code demonstrates how to track a specific component for unnecessary re-renders. By setting the static property `whyDidYouRender` to true, the package will log information whenever `MyComponent` re-renders.
import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';
whyDidYouRender(React);
class MyComponent extends React.Component {
static whyDidYouRender = true;
render() {
return <div>{this.props.text}</div>;
}
}
Customizing Log Output
This code customizes the log output by including and excluding specific components. It also enables logging when prop values change. This helps in fine-tuning the debugging process to focus on specific parts of the application.
import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';
whyDidYouRender(React, {
logOnDifferentValues: true,
include: [/^MyComponent$/],
exclude: [/^SomeOtherComponent$/],
});
react-perf-devtool is a performance monitoring tool for React applications. It provides a visual representation of component render times and helps identify performance bottlenecks. Unlike @welldone-software/why-did-you-render, which focuses on logging re-renders, react-perf-devtool offers a more visual approach to performance monitoring.
react-axe is a tool for auditing React applications for accessibility issues. While it doesn't focus on performance like @welldone-software/why-did-you-render, it helps developers ensure their applications are accessible to all users. It provides real-time feedback on accessibility issues directly in the browser's developer tools.
react-devtools is an official tool from Facebook for inspecting the React component tree. It allows developers to view the state and props of components, as well as track component updates. While it doesn't specifically target unnecessary re-renders, it provides a comprehensive set of tools for debugging React applications.
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 pure component it would always re-render on every element creation:
<BigListPureComponent style={{width: '100%'}}/>
It can also help you to simply track when and why a certain component re-renders.
The latest version of the library was tested (unit tests and E2E) with React@18
only. For React 17 and 16, please use version @^7.
npm install @welldone-software/why-did-you-render --save-dev
or
yarn add --dev @welldone-software/why-did-you-render
If you use the automatic
JSX transformation, set the library to be the import source, and make sure preset-react
is in development
mode.
['@babel/preset-react', {
runtime: 'automatic',
development: process.env.NODE_ENV === 'development',
importSource: '@welldone-software/why-did-you-render',
}]
Unfortunately, the metro-react-native-babel-preset
that comes with react-native out of the box does not allow you to change the options of the babel/plugin-transform-react-jsx
plugin. Just add the plugin with options 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.
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
env: {
development: {
plugins: [['@babel/plugin-transform-react-jsx', { runtime: 'classic' }]],
},
},
}
Notice: Create React App (CRA) ^4 does use 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 first import in your application.
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,
});
}
Notice: The library should NEVER be used in production because it slows down React
In Typescript, call the file wdyr.ts and add the following line to the top of the file to import the package's types:
/// <reference types="@welldone-software/why-did-you-render" />
Import wdyr
as the first import (even before react-hot-loader
):
index.js
:
import './wdyr'; // <--- first import
import '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'));
If you use trackAllPureComponents
like we suggest, all pure components (React.PureComponent or React.memo) will be tracked.
Otherwise, add whyDidYouRender = true
to component classes/functions you want to track. (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
:
import React from 'react';
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']
]
});
}
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:
class BigList extends React.Component {
static whyDidYouRender = true
render(){
return (
//some heavy render you want to ensure doesn't happen if its not necessary
)
}
}
Or for functional components:
const BigListPureComponent = props => (
<div>
//some heavy component you want to ensure doesn't happen if its not necessary
</div>
)
BigListPureComponent.whyDidYouRender = true
You can also pass an object to specify more advanced tracking settings:
EnhancedMenu.whyDidYouRender = {
logOnDifferentValues: true,
customName: 'Menu'
}
logOnDifferentValues
:
Normally, only re-renders that are caused by equal values in props / state trigger notifications:
render(<Menu a={1}/>)
render(<Menu a={1}/>)
This option will trigger notifications even if they occurred because of different props / state (Thus, because of "legit" re-renders):
render(<Menu a={1}/>)
render(<Menu a={2}/>)
customName
:
Sometimes the name of the component can be missing or very inconvenient. For example:
withPropsOnChange(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
notifier: ({Component, displayName, hookName, prevProps, prevState, prevHook, nextProps, nextState, nextHook, 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:
whyDidYouRender(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:
whyDidYouRender(React, {
trackExtraHooks: [
// notice that 'useSelector' is a named export
[ReactRedux, 'useSelector'],
]
});
There is currently a problem with rewriting exports of imported files 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:
render(<BigListPureComponent a={1}/>)
render(<BigListPureComponent a={2}/>)
// 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'
)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
:
const HotApp = hot(App);
HotApp.whyDidYouRender = true;
ReactDOM.render(<HotApp/>, document.getElementById('root'));
ReactDOM.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:
const SimpleComponent = ({a}) => <div data-testid="foo">{a.b}</div>)
// not before the connect:
// SimpleComponent.whyDidYouRender = true
const ConnectedSimpleComponent = connect(
state => ({a: state.a})
)(SimpleComponent)
// after the connect:
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.
FAQs
Monkey patches React to notify you about avoidable re-renders.
The npm package @welldone-software/why-did-you-render receives a total of 405,842 weekly downloads. As such, @welldone-software/why-did-you-render popularity was classified as popular.
We found that @welldone-software/why-did-you-render demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
Security News
Members Hub is conducting large-scale campaigns to artificially boost Discord server metrics, undermining community trust and platform integrity.
Security News
NIST has failed to meet its self-imposed deadline of clearing the NVD's backlog by the end of the fiscal year. Meanwhile, CVE's awaiting analysis have increased by 33% since June.