react-state-orchestration
React Query + Zustand Clean Architecture Patterns
State management architecture for React/Next.js applications using TanStack Query v5 and Zustand 5.
The Boundary
React Query cache ─── server state (single source of truth)
useState ─── local form / ephemeral component state
Zustand ─── client-only UI state (NO server mirrors)
The anti-pattern this replaces:
API → React Query → useEffect hydration → Zustand items[] → Components
fetch mirror store read
This pattern creates two sources of truth, requires sync*ToStore() indirection,
and causes stale-state bugs when components read from Zustand instead of the cache.
When to Apply
Apply these patterns when:
- Writing or reviewing
useQuery/useMutationhooks - Writing or reviewing Zustand store slices
- Implementing optimistic updates with rollback
- Reading server data inside async functions (stale closure risk)
- Deciding whether a piece of data belongs in RQ cache or Zustand
- Refactoring
useEffecthydration that writes to Zustand
Pattern Index
| Priority | Pattern | Impact | Reference File |
|---|---|---|---|
| 1 | State boundary — what goes where | CRITICAL | references/core-principle.md |
| 2 | staleTime: Infinity + gcTime strategy |
HIGH | references/stale-time-gc.md |
| 3 | Adapter hook with select optimization |
HIGH | references/adapter-hook.md |
| 4 | Cache helpers — imperative read/write | HIGH | references/cache-helpers.md |
| 5 | Optimistic update + automatic rollback | HIGH | references/optimistic-updates.md |
| 6 | Zustand session slice for client-only state | MEDIUM | references/zustand-session-slice.md |
| 7 | Anti-patterns to avoid | CRITICAL | references/anti-patterns.md |
Quick Reference
Reading server data in async functions
// WRONG — stale closure from Zustand mirror
const post = useAppStore.getState().posts.find(p => p.id === postId);
// CORRECT — always fresh from React Query cache
const post = getEntityFromCache(queryClient, postId);
Optimistic update with rollback
await withOptimisticUpdate(
queryClient,
postId,
(prev) => ({ ...prev, tags }), // 1. update cache immediately
() => updatePost({ postId, tags }), // 2. persist in background
// auto-rollback to snapshot on error
);
Component: select only what you need
// Re-renders only when title changes, not on any field change
const { data: title } = usePostData(postId, {
select: (d) => d.title,
});
Zustand: client-only state only
// WRONG — server data mirror
store.addPost(postFromServer); // called in useEffect after fetch
store.updatePost(id, serverResponse); // called after mutation success
// CORRECT — transient client state only
store.setDraftContent(postId, content); // local editor state
store.setActiveView('editor'); // UI navigation state
References
- TanStack Query v5 Docs: https://tanstack.com/query/v5/docs
- Zustand 5 Docs: https://zustand.docs.pmnd.rs/
- "Does React Query replace Redux/Zustand?": https://tanstack.com/query/v5/docs/framework/react/guides/does-this-replace-client-state-managers
More from datamktkorea/agent-skills
git-commit
A skill for writing Git commit messages. It follows rules combining Conventional Commits and Gitmoji to maintain a consistent commit history.
12git-pull-request
This skill is used by the AI agent to automatically generate Pull Request (PR) content by analyzing Git commit logs and branch strategies. It activates when the user requests to "Create a PR," "Summarize changes," or specifies a commit range for documentation.
9readme-writing
A skill for writing and optimizing a project's README.md file in accordance with standard conventions and templates. It ensures compliance with standards and maintains documentation quality when creating, reviewing, or refactoring project documentation. This skill is triggered during tasks related to README generation and documentation updates.
6fastapi-best-practices
A skill for following best practices when developing FastAPI applications. It covers guidelines for project structure, code organization, and common patterns to ensure maintainable and scalable FastAPI projects.
3write-meeting-notes
Writes structured meeting notes for team meetings. Use this skill whenever the user provides meeting content — a transcript, script, audio file path, or rough notes — and wants it turned into a proper meeting record. Trigger on phrases like "회의록 써줘", "미팅 노트 정리해줘", "회의 내용 정리", "write meeting notes", or when a user pastes a meeting transcript and says "이거 회의록으로 만들어줘". Also trigger when the user shares a meeting recording or file and asks to summarize or document it.
1