Visualize Component Update

This is a basic hook + Component wrapper for showing when components update due to renders or state updates.

This is primarily useful as a demonstration of how tools like React.memo() affect component rendering. Additionally, it shows how you might not need memoization and that reorganizing your application can improve application performance. Specifically, localizing your applications state with the components that depend on it can help minimize the scope of a rerender.

High-Level

  • ControlledCounter's state is controlled via the parent. Execution its action results in not only it updating, but its siblings and their children updating as well.
  • Counter's state is controlled internally, meaning its action only triggers a rerender of itself.
index.jsx
styles.css
index.html
function useFlashOnUpdate() {
    const ref = React.useRef(null);

    React.useEffect(() => {
        ref.current.style.backgroundColor = "#06d6a0";

        const t = setTimeout(() => {
            ref.current.style.backgroundColor = "";
        }, 100);

        return () => {
            clearTimeout();
        };
    });

    return ref;
}

function FlashOnUpdateChip({ children }) {
    const ref = useFlashOnUpdate();
    return (
        <div className="chip" ref={ref}>
            {children}
        </div>
    );
}

function ControlledCounter({ counter, setCounter }) {
    console.log("ControlledCounter rendered");

    return (
        <FlashOnUpdateChip>
            <div>{counter}</div>
            <button onClick={() => setCounter((c) => c + 1)}>Increment</button>
        </FlashOnUpdateChip>
    );
}

function Counter() {
    console.log("Counter rendered");

    const [counter, setCounter] = React.useState(0);

    return (
        <FlashOnUpdateChip>
            <div>{counter}</div>
            <button onClick={() => setCounter((c) => c + 1)}>Increment</button>
        </FlashOnUpdateChip>
    );
}

function StaticComponent({ children }) {
    return <FlashOnUpdateChip>{children}</FlashOnUpdateChip>;
}

const StaticPureComponent = React.memo(StaticComponent);

function UpdateContainer() {
    const [counter, setCounter] = React.useState(0);

    return (
        <div className="container">
            <Counter />
            <ControlledCounter counter={counter} setCounter={setCounter} />
            <StaticComponent>Static Content</StaticComponent>
            <StaticPureComponent>Static Pure Content</StaticPureComponent>
        </div>
    );
}

ReactDOM.render(<UpdateContainer />, document.querySelector("#root"));
html, body {
    height: 100%;
}

body {
    display: flex;
    justify-content: center;
    align-items: center;
}

.container {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(2, 100px);
}

.chip {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    box-sizing: border-box;
    padding: 12px;
    text-align: center;
    background-color: #272b33;
    border-radius: 4px;
    border: 2px solid #393e46;
    transition: background-color 0.2s ease;
}
<div id="root"></div>

Console