Implement efficient state management with Zustand and React Query
# State Management Patterns for Google Antigravity
Master modern state management patterns for React applications with Google Antigravity IDE.
## Zustand Store Setup
```typescript
// stores/user-store.ts
import { create } from "zustand";
import { devtools, persist, subscribeWithSelector } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
interface User {
id: string;
name: string;
email: string;
role: "user" | "admin";
}
interface UserState {
user: User | null;
isAuthenticated: boolean;
preferences: {
theme: "light" | "dark" | "system";
notifications: boolean;
};
}
interface UserActions {
setUser: (user: User | null) => void;
updatePreferences: (prefs: Partial<UserState["preferences"]>) => void;
logout: () => void;
}
type UserStore = UserState & UserActions;
const initialState: UserState = {
user: null,
isAuthenticated: false,
preferences: {
theme: "system",
notifications: true
}
};
export const useUserStore = create<UserStore>()(
devtools(
persist(
subscribeWithSelector(
immer((set) => ({
...initialState,
setUser: (user) => set((state) => {
state.user = user;
state.isAuthenticated = !!user;
}),
updatePreferences: (prefs) => set((state) => {
Object.assign(state.preferences, prefs);
}),
logout: () => set(() => initialState)
}))
),
{
name: "user-storage",
partialize: (state) => ({
preferences: state.preferences
})
}
),
{ name: "UserStore" }
)
);
// Selectors for performance
export const selectUser = (state: UserStore) => state.user;
export const selectIsAdmin = (state: UserStore) => state.user?.role === "admin";
export const selectTheme = (state: UserStore) => state.preferences.theme;
```
## React Query Integration
```typescript
// lib/queries/users.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { api } from "../api-client";
// Query keys factory
export const userKeys = {
all: ["users"] as const,
lists: () => [...userKeys.all, "list"] as const,
list: (filters: Record<string, unknown>) => [...userKeys.lists(), filters] as const,
details: () => [...userKeys.all, "detail"] as const,
detail: (id: string) => [...userKeys.details(), id] as const
};
// Fetch users with filters
export function useUsers(filters: { role?: string; search?: string } = {}) {
return useQuery({
queryKey: userKeys.list(filters),
queryFn: () => api.users.list(filters),
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 30 * 60 * 1000 // 30 minutes
});
}
// Fetch single user
export function useUser(id: string) {
return useQuery({
queryKey: userKeys.detail(id),
queryFn: () => api.users.get(id),
enabled: !!id
});
}
// Create user mutation
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: api.users.create,
onSuccess: (newUser) => {
// Invalidate list queries
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
// Optimistically add to cache
queryClient.setQueryData(userKeys.detail(newUser.id), newUser);
}
});
}
// Update user with optimistic updates
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: string; data: Partial<User> }) =>
api.users.update(id, data),
onMutate: async ({ id, data }) => {
await queryClient.cancelQueries({ queryKey: userKeys.detail(id) });
const previousUser = queryClient.getQueryData(userKeys.detail(id));
queryClient.setQueryData(userKeys.detail(id), (old: User) => ({
...old,
...data
}));
return { previousUser };
},
onError: (err, { id }, context) => {
if (context?.previousUser) {
queryClient.setQueryData(userKeys.detail(id), context.previousUser);
}
},
onSettled: (_, __, { id }) => {
queryClient.invalidateQueries({ queryKey: userKeys.detail(id) });
}
});
}
// Delete user
export function useDeleteUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: api.users.delete,
onSuccess: (_, deletedId) => {
queryClient.removeQueries({ queryKey: userKeys.detail(deletedId) });
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
}
});
}
```
## Combining Zustand with React Query
```typescript
// hooks/useAuth.ts
import { useCallback } from "react";
import { useQueryClient, useMutation } from "@tanstack/react-query";
import { useUserStore, selectUser } from "../stores/user-store";
import { api } from "../lib/api-client";
export function useAuth() {
const queryClient = useQueryClient();
const user = useUserStore(selectUser);
const { setUser, logout: clearStore } = useUserStore();
const loginMutation = useMutation({
mutationFn: api.auth.login,
onSuccess: (data) => {
setUser(data.user);
// Pre-populate user cache
queryClient.setQueryData(["users", "detail", data.user.id], data.user);
}
});
const logout = useCallback(async () => {
await api.auth.logout();
clearStore();
queryClient.clear();
}, [clearStore, queryClient]);
return {
user,
isAuthenticated: !!user,
login: loginMutation.mutate,
loginAsync: loginMutation.mutateAsync,
isLoggingIn: loginMutation.isPending,
loginError: loginMutation.error,
logout
};
}
```
## Global State Subscriptions
```typescript
// lib/store-subscriptions.ts
import { useUserStore } from "../stores/user-store";
// Subscribe to theme changes
useUserStore.subscribe(
(state) => state.preferences.theme,
(theme) => {
document.documentElement.setAttribute("data-theme", theme);
},
{ fireImmediately: true }
);
// Sync auth state with analytics
useUserStore.subscribe(
(state) => state.user,
(user) => {
if (user) {
analytics.identify(user.id, { email: user.email });
} else {
analytics.reset();
}
}
);
```
## Best Practices
1. **Separate server state** (React Query) from client state (Zustand)
2. **Use selectors** to prevent unnecessary re-renders
3. **Implement optimistic updates** for better UX
4. **Create query key factories** for consistent cache management
5. **Use middleware** for devtools, persistence, and immer
6. **Subscribe to store changes** for side effects
7. **Keep stores small and focused**
Google Antigravity provides intelligent state management suggestions and helps optimize re-render patterns.This state-management prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their state-management implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For state-management projects, consider mentioning your framework version, coding style, and any specific libraries you're using.