useMemo vs useCallback: 참조 평등을 표시하는 도구를 만들었습니다

작성자

카테고리:

← 피드로
DEV Community · Devanshu Biswas · 2026-06-27 개발(SW)

Devanshu Biswas

useMemo and useCallback confuse people because the thing they fix is invisible: object and function identity. So I built a tool that makes identity visible — it stamps a number on every reference so you can watch it stay stable or change, and counts when a React.memo child actually re-renders.

▶ Live demo: https://usememo-vs-usecallback.vercel.app/
Source (React 19 + TS): https://github.com/dev48v/usememo-vs-usecallback

The one fact everything hinges on

Every time a component renders, an inline object or function is a brand-new value:

// new {} and new () => {} on EVERY render
<Child config={{ mult }} onAction={() => doThing(mult)} />

Enter fullscreen mode Exit fullscreen mode

React.memo compares props by reference (Object.is). So even though {mult: 2} looks identical to last render’s {mult: 2}, it’s a different object — and the memoized child re-renders anyway. One inline prop silently defeats memo.

The tool gives each reference a visible id. Click “re-render parent” with no memoization and you’ll see config #5 → #6 → #7… while the child’s render counter climbs in lockstep.

What useMemo / useCallback actually do

They cache the reference between renders:

const config   = useMemo(() => ({ mult }), [mult]);      // same object until mult changes
const onAction = useCallback(() => doThing(mult), [mult]); // same function until mult changes

Enter fullscreen mode Exit fullscreen mode

Flip the toggles in the demo and the ids stop changing — so the memo child finally skips. Its render counter freezes no matter how many times the parent renders.

The subtlety people miss

Stable doesn’t mean frozen. Bump the dependency (mult) and the memoized references do change — because the value they represent actually changed. That’s correct: you want a new reference exactly when the data is new, and not a moment sooner. Watching the ids change only on a real dep change is what makes this click.

And the punchline: useCallback(fn, deps) is just useMemo(() => fn, deps). One memoizes a value, the other memoizes a function. Same machine.

Why a visualizer

You can read “memo does a shallow reference comparison” and still ship style={{...}} into a memoized component and wonder why it never skips. Seeing the reference id tick up on every render — then freeze the moment you wrap it — turns the rule into intuition.

Real React, zero UI dependencies. If it helped, a star helps others find it: https://github.com/dev48v/usememo-vs-usecallback

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다