JAVASCRIPT
React useWhyDidYouUpdate Hook for Component Re-render Debugging
Debug unnecessary React component re-renders with `useWhyDidYouUpdate`, a custom hook that logs prop and state changes causing updates, improving performance.
import { useEffect, useRef } from 'react';
// A hook that logs when props or state change, useful for debugging re-renders.
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;
});
}
// Example Usage:
/*
import React, { useState } from 'react';
import useWhyDidYouUpdate from './useWhyDidYouUpdate'; // Assuming useWhyDidYouUpdate is in its own file
function MyComponent(props) {
useWhyDidYouUpdate('MyComponent', props); // Pass component name and props
const [count, setCount] = useState(0);
useWhyDidYouUpdate('MyComponentState', { count }); // Can also check state
return (
<div>
<h1>Hello, {props.name}!</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
);
}
// In a parent component:
// function App() {
// const [name, setName] = useState("World");
// const [show, setShow] = useState(true);
//
// return (
// <div>
// <button onClick={() => setName(name === "World" ? "React" : "World")}>Toggle Name</button>
// <button onClick={() => setShow(!show)}>Toggle Component</button>
// {show && <MyComponent name={name} />}
// </div>
// );
// }
*/
How it works: The `useWhyDidYouUpdate` hook helps debug unnecessary component re-renders. It takes a component `name` and its `props` as arguments. Using `useRef`, it keeps track of the `props` from the previous render. In a `useEffect` without dependencies (running after every render), it compares the current `props` with the `previousProps`. If any prop value has changed (using a shallow comparison), it logs the component name and the specific props (and their old/new values) that caused the update to the console, making it easier to identify performance bottlenecks.