Security News
Supply Chain Attack Detected in Solana's web3.js Library
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
@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 avoidable re-renders. (Works with React Native
as well.)
For example, when you pass style={{width: '100%'}}
to a big pure component and make it always re-render:
It can also help you to simply track when and why a certain component re-renders.
The last version of the library has been tested with
React@16.13.1
but it is expected to work with allReact@16
versions.
For versions before 16.8 try turning off hooks support by using
trackHooks: false
inwhyDidYouRender
's init options.*
npm install @welldone-software/why-did-you-render --save
Execute whyDidYouRender
as the first thing that happens in your application (even before react-hot-loader
).
The best way of doing this would be to create a file (lets say wdyr.js
) near the entrypoint of 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,
});
}
And then import wdyr.js
(even before react-hot-loader
):
index.js
:
import './wdyr';
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 the latest react-redux
with hooks (or any other custom library), you can also patch it like this:
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']
]
});
}
But there is currently a problem with rewriting exports of imported files in webpack
and a small workaround should be applied to support this feature #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 (components that are extending React.PureComponent, or function components that are wrapped with 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
)
}
}
And 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 very inconvenient. For example:
const EnhancedMenu = withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(lifecycle(withPropsOnChange(withPropsOnChange(onlyUpdateForKeys(LoadNamespace(Connect(withState(withState(withPropsOnChange(lifecycle(withPropsOnChange(withHandlers(withHandlers(withHandlers(withHandlers(Connect(lifecycle(Menu)))))))))))))))))))))))
will have the display name:
withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(lifecycle(withPropsOnChange(withPropsOnChange(onlyUpdateForKeys(LoadNamespace(Connect(withState(withState(withPropsOnChange(lifecycle(withPropsOnChange(withHandlers(withHandlers(withHandlers(withHandlers(Connect(lifecycle(Menu)))))))))))))))))))))))
To prevent polluting the console, and any other reason, you can change it using customName
.
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: []
logOnDifferentValues: false
logOwnerReasons: false
hotReloadBufferMs: 500
onlyLogs: false
collapseGroups: false
titleColor
diffNameColor
diffPathColor
notifier: ({Component, displayName, prevProps, prevState, nextProps, nextState, reason, options}) => void
You can include or exclude tracking for re-renders for components
by their displayName with the include
and exclude
options.
Notice: exclude takes priority over both include
and whyDidYouRender
statics on components.
For example, the following code is used to track all redundant re-renders that are caused by React-Redux:
whyDidYouRender(React, { include: [/^ConnectFunction/] });
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
.
You can turn off tracking of hooks changes.
Understand and fix hook issues.
Adding extra hooks to track for "redundant" results:
whyDidYouRender(React, {
trackExtraHooks: [
[ReactRedux, 'useSelector']
]
});
There is currently a problem with rewriting exports of imported files in webpack.
To see available workarounds check out the discussion at bug #85 - trackExtraHooks cannot set property
Normally, you only want notifications about component re-renders when their props and state are the same, because it means these re-renders could have been avoided. But you can also track all re-renders, even on different state/props.
render(<BigListPureComponent a={1}/>)
render(<BigListPureComponent a={2}/>)
// this will only cause whyDidYouRender notifications for {logOnDifferentValues: true}
One way of fixing re-render issues is preventing the component's owner from re-rendering.
To make that easier, you can use logOwnerReasons: true
to view the reasons why owner component re-renders.
Time in milliseconds to ignore updates after a hot reload is detected.
We can't currently know exactly if a render was triggered by hot reload,
so instead, we ignore all updates for hotReloadBufferMs
(default: 500) after a hot reload.
If you don't want to use console.group
to group logs by component, you can print them as simple logs.
Grouped logs can start collapsed:
Controls the colors used in the console notifications
You can create a custom notifier if the default one does not suite your needs.
Class constructors must be invoked with 'new'
.If you are building for latest browsers (or using es6 classes without building) you don't transpile the "class" keyword.
This causes an error because the library uses transpiled classes, and transpiled classes currently can't extend native classes.
To fix this, use the "no-classes-transpile" dist:
import React from 'react';
if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render/dist/no-classes-transpile/umd/whyDidYouRender.min.js');
whyDidYouRender(React);
}
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
Inspired by the following previous work:
https://github.com/maicki/why-did-you-update which I had the chance to maintain for some time.
https://github.com/garbles/why-did-you-update where A deep dive into React perf debugging is credited for the idea.
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 308,884 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
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.