useMemo 캐시 적중 진단 — 매 렌더 새로 만들어지는 deps
useMemo가 실제로 재사용되고 있는지 계산 횟수로 확인한다.
언제: 무거운 계산을 useMemo로 감쌌는데 렌더 시간이 줄지 않을 때.
절차
-
계산 함수 안에 카운터를 넣는다.
const rows = useMemo(() => { console.count('calc') return items.filter(fn) }, [items]) -
리렌더를 몇 번 일으키고
calc가 렌더와 같이 늘어나는지 본다. -
같이 늘어나면
deps를 의심한다. React는 재료 안을 들여다보지 않고Object.is로 같은 물건인지만 보므로,1 매 렌더 새로 만들어지는 값이deps에 있으면 한 번도 재사용되지 않는다.<List items={data.map(transform)} /> -
넘기는 쪽에서 참조를 붙잡아 둔다.
const items = useMemo(() => data.map(transform), [data])
확인: calc가 렌더 횟수와 따로 논다 — data가 바뀔 때만 늘어난다. 개발 모드에서는 React가 계산 함수를 일부러 두 번 부르므로 렌더당 2씩 뛰는 게 정상이다. 보는 건 절대값이 아니라 렌더를 따라 늘어나는지다.
함정
- 재사용을 못 하는 건 느린 것이지 틀린 것이 아니다. 같은 재료로 늘 같은 답을 내는 계산이라면 답은 똑같다. 아래 둘만 답을 바꾼다.
deps에 안 적어둔 값을 계산 안에서 쓰면(stale closure) 그 값이 바뀌어도 React는 “재료 그대로”로 보고 옛날 답을 내놓는다. 게다가 무관한 재료가 바뀌는 순간 우연히 맞아졌다가 다시 틀린다 — 그래서 원인을 찾기 어렵다.- 계산이 매번 다른 답을 내면(랜덤 같은 것) React가 특별한 이유로 기억해둔 걸 버리는 순간 답이 바뀐다 → 574
Footnotes
-
“React will compare each dependency with its previous value using the
Object.iscomparison.” / “React will not throw away the cached value unless there is a specific reason to do that … This should be fine if you rely onuseMemosolely as a performance optimization.” / “In Strict Mode, React will call your calculation function twice in order to help you find accidental impurities.” (useMemo — React) ↩