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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs