| mode | description |
|---|---|
ask |
Create a new Zustand store with subscribeWithSelector middleware |
Create a new Zustand store following CoreAI DIY patterns.
STORE_NAME: The store name (e.g.,settings)STORE_DESCRIPTION: Brief description of what the store managesSTATE_FIELDS: Key state fieldsACTIONS: Key actions the store needs
Create src/frontend/src/store/${STORE_NAME}-store.ts:
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';
// State interface
export interface ${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}State {
// Add ${STATE_FIELDS}
isLoading: boolean;
error: string | null;
}
// Actions interface
export interface ${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Actions {
// Add ${ACTIONS}
setLoading: (loading: boolean) => void;
setError: (error: string | null) => void;
reset: () => void;
}
// Combined store type
export type ${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store =
${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}State &
${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Actions;
// Initial state
const initialState: ${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}State = {
// Initialize ${STATE_FIELDS}
isLoading: false,
error: null,
};
// Create store with subscribeWithSelector
export const use${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store = create<${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store>()(
subscribeWithSelector((set, get) => ({
...initialState,
// Add action implementations
setLoading: (loading) => set({ isLoading: loading }),
setError: (error) => set({ error }),
reset: () => set(initialState),
}))
);Add to src/frontend/src/store/index.ts:
export { use${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store } from './${STORE_NAME}-store';
export type {
${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}State,
${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Actions,
${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store,
} from './${STORE_NAME}-store';import { use${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store } from '@/store';
function MyComponent() {
// Select specific state (prevents unnecessary re-renders)
const value = use${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store((state) => state.value);
const action = use${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store((state) => state.action);
// Or use multiple selectors
const { value, action } = use${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store((state) => ({
value: state.value,
action: state.action,
}));
return <button onClick={() => action(newValue)}>Update</button>;
}// Subscribe to specific state changes
use${STORE_NAME.charAt(0).toUpperCase() + STORE_NAME.slice(1)}Store.subscribe(
(state) => state.value,
(value, prevValue) => {
console.log('Value changed from', prevValue, 'to', value);
}
);loadData: async () => {
set({ isLoading: true, error: null });
try {
const data = await fetchData();
set({ data, isLoading: false });
} catch (error) {
set({ error: error.message, isLoading: false });
}
},// Use get() to access current state
getFilteredItems: () => {
const { items, filter } = get();
return items.filter(item => item.matches(filter));
},// Single set() call for multiple updates
updateMultiple: (updates) => {
set({
field1: updates.field1,
field2: updates.field2,
field3: updates.field3,
});
},- Store file created with subscribeWithSelector
- State interface defined
- Actions interface defined
- Initial state defined
- Exported from barrel
- Tests added