JAVASCRIPT
Debug Component Re-renders with useWhyDidYouUpdate
A React hook that logs precisely why a component re-rendered by comparing current and previous props/state, an essential tool for performance debugging and optimization.
import { useRef, useEffect } from 'react';
function useWhyDidYouUpdate(name, props) {
const previousProps = useRef();
useEffect(() => {
if (previousProps.current) {
const allKeys = Object.keys({ ...previousProps.current, ...props });
const changesObj = {};
allKeys.forEach((key) => {
if (previousProps.current[key] !== props[key]) {
changesObj[key] = {
from: previousProps.current[key],
to: props[key],
};
}
});
if (Object.keys(changesObj).length) {
console.log('[why-did-you-update]', name, changesObj);
}
}
previousProps.current = props;
});
}
export default useWhyDidYouUpdate;
/*
// Example Usage:
// To use effectively for deep comparisons, install 'npm i react-fast-compare'
// import { memo, useState, useEffect } from 'react';
// import isEqual from 'react-fast-compare';
// // For components with complex object/array props, use memo with a deep comparison
// const MemoizedChildComponent = memo(ChildComponent, isEqual);
// function ParentComponent() {
// const [count, setCount] = useState(0);
// const [text, setText] = useState('hello');
// const [user, setUser] = useState({ name: 'Alice' });
// useEffect(() => {
// const interval1 = setInterval(() => setCount(c => c + 1), 5000);
// const interval2 = setInterval(() => setText(t => t === 'hello' ? 'world' : 'hello'), 7000);
// const interval3 = setInterval(() => setUser(u => ({ ...u, age: Math.floor(Math.random() * 100) })), 9000);
// return () => {
// clearInterval(interval1);
// clearInterval(interval2);
// clearInterval(interval3);
// };
// }, []);
// return (
// <div>
// <ChildComponent count={count} text={text} user={user} />
// <p>Parent count: {count}</p>
// </div>
// );
// }
// function ChildComponent({ count, text, user }) {
// useWhyDidYouUpdate('ChildComponent', { count, text, user });
// return (
// <div>
// <p>Child Count: {count}</p>
// <p>Child Text: {text}</p>
// <p>Child User: {user.name} ({user.age || 'N/A'})</p>
// </div>
// );
// }
*/
How it works: The `useWhyDidYouUpdate` hook helps diagnose unnecessary component re-renders, a common performance bottleneck in React applications. It takes a component `name` and its `props`. On each render, it compares the current `props` with the `props` from the previous render, which are stored in a `useRef`. If any prop has changed (using strict equality `!==`), it logs the component name and details of the changed props (their old and new values) to the console, making it easier to pinpoint the cause of unwanted updates. For deep comparison of object or array props, an external utility like `react-fast-compare` can be integrated with `memo`.