Skip to main content 홈 크리에이터 thebushidocollective han zustand-advanced-patterns
zustand-advanced-patterns Use when implementing advanced Zustand patterns including transient updates, subscriptions with selectors, store composition, and performance optimization techniques.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/TheBushidoCollective/han --skill zustand-advanced-patterns명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... name zustand-advanced-patterns user-invocable false description Use when implementing advanced Zustand patterns including transient updates, subscriptions with selectors, store composition, and performance optimization techniques. allowed-tools ["Read","Write","Edit","Bash","Grep","Glob"]
Zustand - Advanced Patterns
Advanced techniques and patterns for building complex applications with Zustand, including transient updates, optimistic updates, and sophisticated state management strategies.
Key Concepts
Transient Updates
Update state without triggering re-renders:
const useStore = create ((set ) => ({
count : 0 ,
increment : () =>
set ((state ) => ({ count : state.count + 1 }), false , 'increment' ),
}))
useStore. ({ : }, )
setState
count
10
true
Subscriptions with Selectors Subscribe to specific slices of state:
const useStore = create<Store >()((set ) => ({ }))
const unsubscribe = useStore.subscribe (
(state ) => state.count ,
(count, prevCount ) => {
console .log (`Count changed from ${prevCount} to ${count} ` )
},
{
equalityFn : (a, b ) => a === b,
fireImmediately : false ,
}
)
Best Practices
1. Optimistic Updates Update UI immediately, then sync with server:
interface TodoStore {
todos : Todo []
addTodo : (text : string ) => Promise <void >
updateTodo : (id : string , text : string ) => Promise <void >
deleteTodo : (id : string ) => Promise <void >
}
const useTodoStore = create<TodoStore >()((set, get ) => ({
todos : [],
addTodo : async (text) => {
const optimisticTodo = {
id : `temp-${Date .now()} ` ,
text,
completed : false ,
}
set ((state ) => ({
todos : [...state.todos , optimisticTodo],
}))
try {
const savedTodo = await api.createTodo ({ text })
set ((state ) => ({
todos : state.todos .map ((todo ) =>
todo.id === optimisticTodo.id ? savedTodo : todo
),
}))
} catch (error) {
set ((state ) => ({
todos : state.todos .filter ((todo ) => todo.id !== optimisticTodo.id ),
}))
throw error
}
},
updateTodo : async (id, text) => {
const previousTodos = get ().todos
set ((state ) => ({
todos : state.todos .map ((todo ) =>
todo.id === id ? { ...todo, text } : todo
),
}))
try {
await api.updateTodo (id, { text })
} catch (error) {
set ({ todos : previousTodos })
throw error
}
},
deleteTodo : async (id) => {
const previousTodos = get ().todos
set ((state ) => ({
todos : state.todos .filter ((todo ) => todo.id !== id),
}))
try {
await api.deleteTodo (id)
} catch (error) {
set ({ todos : previousTodos })
throw error
}
},
}))
2. Undo/Redo Pattern Implement time-travel functionality:
interface HistoryState <T> {
past : T[]
present : T
future : T[]
}
interface HistoryStore <T> {
history : HistoryState <T>
canUndo : boolean
canRedo : boolean
set : (newPresent : T ) => void
undo : () => void
redo : () => void
reset : (initialState : T ) => void
}
function createHistoryStore<T>(initialState : T) {
return create<HistoryStore <T>>()((set, get ) => ({
history : {
past : [],
present : initialState,
future : [],
},
get canUndo () {
return get ().history .past .length > 0
},
get canRedo () {
return get ().history .future .length > 0
},
set : (newPresent ) =>
set ((state ) => ({
history : {
past : [...state.history .past , state.history .present ],
present : newPresent,
future : [],
},
})),
undo : () =>
set ((state ) => {
if (state.history .past .length === 0 ) return state
const previous = state.history .past [state.history .past .length - 1 ]
const newPast = state.history .past .slice (0 , -1 )
return {
history : {
past : newPast,
present : previous,
future : [state.history .present , ...state.history .future ],
},
}
}),
redo : () =>
set ((state ) => {
if (state.history .future .length === 0 ) return state
const next = state.history .future [0 ]
const newFuture = state.history .future .slice (1 )
return {
history : {
past : [...state.history .past , state.history .present ],
present : next,
future : newFuture,
},
}
}),
reset : (initialState ) =>
set ({
history : {
past : [],
present : initialState,
future : [],
},
}),
}))
}
interface CanvasState {
shapes : Shape []
selectedId : string | null
}
const useCanvasStore = createHistoryStore<CanvasState >({
shapes : [],
selectedId : null ,
})
function Canvas ( ) {
const { present } = useCanvasStore ((state ) => state.history )
const { canUndo, canRedo, undo, redo } = useCanvasStore ()
return (
<div >
<button onClick ={undo} disabled ={!canUndo} >
Undo
</button >
<button onClick ={redo} disabled ={!canRedo} >
Redo
</button >
{/* Render canvas */}
</div >
)
}
3. Store Composition Compose multiple stores together:
import { create, StoreApi } from 'zustand'
function createBoundStore ( ) {
const useAuthStore = create<AuthStore >()((set, get ) => ({
user : null ,
login : async (credentials) => {
const user = await api.login (credentials)
set ({ user })
const cartStore = stores.cart .getState ()
await cartStore.syncCart ()
},
logout : () => {
set ({ user : null })
stores.cart .getState ().clearCart ()
},
}))
const useCartStore = create<CartStore >()((set, get ) => ({
items : [],
addItem : (item ) =>
set ((state ) => ({ items : [...state.items , item] })),
clearCart : () => set ({ items : [] }),
syncCart : async () => {
const user = stores.auth .getState ().user
if (!user) return
const items = await api.fetchCart (user.id )
set ({ items })
},
}))
return {
auth : useAuthStore,
cart : useCartStore,
}
}
const stores = createBoundStore ()
export const useAuthStore = stores.auth
export const useCartStore = stores.cart
4. React Context Integration Use Zustand with React Context for scoped stores:
import { createContext, useContext, useRef } from 'react'
import { createStore, useStore } from 'zustand'
interface TodoStore {
todos : Todo []
addTodo : (text : string ) => void
toggleTodo : (id : string ) => void
}
type TodoStoreApi = ReturnType <typeof createTodoStore>
const createTodoStore = (initialTodos : Todo [] = [] ) => {
return createStore<TodoStore >()((set ) => ({
todos : initialTodos,
addTodo : (text ) =>
set ((state ) => ({
todos : [
...state.todos ,
{ id : Date .now ().toString (), text, completed : false },
],
})),
toggleTodo : (id ) =>
set ((state ) => ({
todos : state.todos .map ((todo ) =>
todo.id === id ? { ...todo, completed : !todo.completed } : todo
),
})),
}))
}
const TodoStoreContext = createContext<TodoStoreApi | null >(null )
export function TodoStoreProvider ({
children,
initialTodos,
}: {
children: React.ReactNode
initialTodos?: Todo[]
} ) {
const storeRef = useRef<TodoStoreApi >()
if (!storeRef.current ) {
storeRef.current = createTodoStore (initialTodos)
}
return (
<TodoStoreContext.Provider value ={storeRef.current} >
{children}
</TodoStoreContext.Provider >
)
}
export function useTodoStore<T>(selector : (state : TodoStore ) => T): T {
const store = useContext (TodoStoreContext )
if (!store) {
throw new Error ('useTodoStore must be used within TodoStoreProvider' )
}
return useStore (store, selector)
}
function App ( ) {
return (
<TodoStoreProvider initialTodos ={[]} >
<TodoList />
</TodoStoreProvider >
)
}
function TodoList ( ) {
const todos = useTodoStore ((state ) => state.todos )
const addTodo = useTodoStore ((state ) => state.addTodo )
return (
<div >
{todos.map((todo) => (
<div key ={todo.id} > {todo.text}</div >
))}
<button onClick ={() => addTodo('New todo')}>Add</button >
</div >
)
}
5. Derived State with Selectors Create memoized derived state:
import { create } from 'zustand'
import { shallow } from 'zustand/shallow'
interface Store {
items : Item []
filter : 'all' | 'active' | 'completed'
sortBy : 'name' | 'date'
}
const useStore = create<Store >()((set ) => ({ }))
const selectFilteredAndSortedItems = (state : Store ) => {
let items = state.items
if (state.filter === 'active' ) {
items = items.filter ((item ) => !item.completed )
} else if (state.filter === 'completed' ) {
items = items.filter ((item ) => item.completed )
}
if (state.sortBy === 'name' ) {
items = [...items].sort ((a, b ) => a.name .localeCompare (b.name ))
} else {
items = [...items].sort ((a, b ) => b.date .getTime () - a.date .getTime ())
}
return items
}
function ItemList ( ) {
const items = useStore (selectFilteredAndSortedItems)
return <div > {items.map((item) => <Item key ={item.id} item ={item} /> )}</div >
}
Examples
WebSocket Integration interface ChatStore {
messages : Message []
isConnected : boolean
connect : () => void
disconnect : () => void
sendMessage : (text : string ) => void
}
const useChatStore = create<ChatStore >()((set, get ) => {
let ws : WebSocket | null = null
return {
messages : [],
isConnected : false ,
connect : () => {
ws = new WebSocket ('wss://chat.example.com' )
ws.onopen = () => {
set ({ isConnected : true })
}
ws.onmessage = (event ) => {
const message = JSON .parse (event.data )
set ((state ) => ({
messages : [...state.messages , message],
}))
}
ws.onclose = () => {
set ({ isConnected : false })
}
ws.onerror = (error ) => {
console .error ('WebSocket error:' , error)
set ({ isConnected : false })
}
},
disconnect : () => {
ws?.close ()
ws = null
set ({ isConnected : false })
},
sendMessage : (text ) => {
if (!ws || ws.readyState !== WebSocket .OPEN ) return
const message = {
id : Date .now ().toString (),
text,
timestamp : new Date (),
userId : 'current-user' ,
}
ws.send (JSON .stringify (message))
set ((state ) => ({
messages : [...state.messages , message],
}))
},
}
})
Pagination Pattern interface PaginatedStore <T> {
items : T[]
page : number
pageSize : number
total : number
isLoading : boolean
hasMore : boolean
fetchPage : (page : number ) => Promise <void >
nextPage : () => Promise <void >
prevPage : () => Promise <void >
reset : () => void
}
function createPaginatedStore<T>(
fetcher : (page : number , pageSize : number ) => Promise <{ items : T[]; total : number }>,
pageSize : number = 20
) {
return create<PaginatedStore <T>>()((set, get ) => ({
items : [],
page : 1 ,
pageSize,
total : 0 ,
isLoading : false ,
get hasMore () {
const { page, pageSize, total } = get ()
return page * pageSize < total
},
fetchPage : async (page) => {
set ({ isLoading : true })
try {
const { items, total } = await fetcher (page, get ().pageSize )
set ({ items, page, total, isLoading : false })
} catch (error) {
set ({ isLoading : false })
throw error
}
},
nextPage : async () => {
const { page, hasMore } = get ()
if (!hasMore) return
await get ().fetchPage (page + 1 )
},
prevPage : async () => {
const { page } = get ()
if (page <= 1 ) return
await get ().fetchPage (page - 1 )
},
reset : () =>
set ({
items : [],
page : 1 ,
total : 0 ,
isLoading : false ,
}),
}))
}
const useProductStore = createPaginatedStore<Product >(
async (page, pageSize) => {
const response = await fetch (
`/api/products?page=${page} &pageSize=${pageSize} `
)
return response.json ()
}
)
Computed Properties with Getters interface Store {
items : Item []
filter : string
sortBy : string
filteredItems : Item []
sortedItems : Item []
stats : {
total : number
completed : number
active : number
}
}
const useStore = create<Store >()((set, get ) => ({
items : [],
filter : 'all' ,
sortBy : 'date' ,
get filteredItems () {
const { items, filter } = get ()
if (filter === 'all' ) return items
if (filter === 'completed' ) return items.filter ((i ) => i.completed )
return items.filter ((i ) => !i.completed )
},
get sortedItems () {
const { filteredItems, sortBy } = get ()
const items = [...filteredItems]
if (sortBy === 'name' ) {
return items.sort ((a, b ) => a.name .localeCompare (b.name ))
}
return items.sort ((a, b ) => b.date .getTime () - a.date .getTime ())
},
get stats () {
const { items } = get ()
return {
total : items.length ,
completed : items.filter ((i ) => i.completed ).length ,
active : items.filter ((i ) => !i.completed ).length ,
}
},
}))
Common Patterns
Batched Updates Update multiple stores atomically:
function batchUpdates (updates : Array <() => void > ) {
updates.forEach ((update ) => update ())
}
batchUpdates ([
() => useAuthStore.setState ({ user : newUser }),
() => useCartStore.setState ({ items : [] }),
() => useNotificationStore.setState ({ unread : 0 }),
])
Error Boundary Integration interface ErrorStore {
errors : Error []
addError : (error : Error ) => void
clearErrors : () => void
}
const useErrorStore = create<ErrorStore >()((set ) => ({
errors : [],
addError : (error ) =>
set ((state ) => ({ errors : [...state.errors , error] })),
clearErrors : () => set ({ errors : [] }),
}))
function ErrorBoundary ({ children }: { children: React.ReactNode } ) {
const errors = useErrorStore ((state ) => state.errors )
if (errors.length > 0 ) {
return <div > Error: {errors[0].message}</div >
}
return <> {children}</>
}
Anti-Patterns
❌ Don't Store Derived State
const useStore = create ((set ) => ({
items : [],
itemCount : 0 ,
addItem : (item ) =>
set ((state ) => ({
items : [...state.items , item],
itemCount : state.items .length + 1 ,
})),
}))
const useStore = create ((set, get ) => ({
items : [],
get itemCount () {
return get ().items .length
},
addItem : (item ) =>
set ((state ) => ({ items : [...state.items , item] })),
}))
❌ Don't Create Circular Dependencies
const useStoreA = create ((set ) => ({
value : 0 ,
update : () => {
useStoreB.getState ().sync ()
},
}))
const useStoreB = create ((set ) => ({
value : 0 ,
sync : () => {
useStoreA.getState ().update ()
},
}))
❌ Don't Overuse Subscriptions
function Component ( ) {
useEffect (() => {
const unsubscribe = useStore.subscribe ((state ) => {
console .log (state)
})
}, [])
}
function Component ( ) {
const value = useStore ((state ) => state.value )
return <div > {value}</div >
}
Related Skills
zustand-store-patterns : Basic store creation and usage
zustand-typescript : TypeScript integration
zustand-middleware : Using middleware for enhanced functionality