"매 right tool 의 매 right granularity". 매 React 19 (2026) 시대, Redux 의 boilerplate 없이 매 selective subscription 으로 re-render 최소화 — Zustand (store-based, 매 simple), Jotai (atomic, 매 fine-grained), Valtio (proxy-based, 매 mutable feel) 의 매 성격 다른 trio.
매 핵심
매 세 라이브러리 비교
Zustand: 단일 store + selector subscription. 매 Redux replacement 의 80% 케이스. ~3kb.
Jotai: atom 단위 state, atom 의 dependency graph. 매 fine-grained re-render. ~5kb.
Valtio: proxy 로 mutable mutation, 매 reactive subscribe. ~3kb. 매 immer 대체.
매 셋 모두 React 19 의 useSyncExternalStore 와 매 native concurrent rendering 지원.
매 선택 축
Granularity: Jotai (atom) > Valtio (proxy path) > Zustand (selector).
import{atom,useAtom,useAtomValue}from'jotai';constcountAtom=atom(0);constdoubledAtom=atom((get)=>get(countAtom)*2);constasyncUserAtom=atom(async(get)=>{constid=get(userIdAtom);returnfetch(`/api/u/${id}`).then(r=>r.json());});functionCounter() {const[count,setCount]=useAtom(countAtom);constdoubled=useAtomValue(doubledAtom);// re-renders only when doubled changes
return<buttononClick={()=>setCount(c=>c+1)}>{doubled}</button>;}
Jotai atomFamily (dynamic per-id atoms)
import{atomFamily}from'jotai/utils';consttodoAtomFamily=atomFamily((id: string)=>atom({id,text:'',done: false}),);functionTodo({id}:{id: string}){const[todo,setTodo]=useAtom(todoAtomFamily(id));// only this Todo re-renders when its atom changes
}
언제: store shape 의 first scaffold, selector boilerplate 생성, 매 atom dependency graph 시각화.
언제 X: 매 actual perf 측정 — profiler 만 truth. Library choice 의 매 nuanced trade-off 도 매 LLM 의 over-confident 영역.
❌ 안티패턴
Single mega-store (Zustand): selector 로 mitigate 가능하지만, atom split 가 더 clean.
Atom explosion (Jotai): 매 trivial state 의 atom 화 → 매 cognitive overhead.
Direct Valtio mutation in render: 매 React rule violation. mutation 은 event handler 또는 effect 내.
Context for high-frequency state: 매 entire subtree re-render. 매 use these libs instead.
Mixing server + client state: 매 TanStack Query 의 영역, store 에 넣지 마.