Claude Code से React State Management: Context से TanStack Query तक
Claude Code से React state management सुधारें: Context, Zustand, Jotai, TanStack Query, tests और सुरक्षित prompts।
React state management तब मुश्किल होता है जब हर बदलने वाली चीज को एक जैसा माना जाता है। input value, modal open state, cart, theme preference और API से आई product list सभी state हैं, लेकिन उनका owner और lifetime अलग है।
Claude Code इस code को साफ कर सकता है, पर prompt साफ होना चाहिए। “state management improve करो” कहने से बड़ा global store, duplicate cache या बिना test की library migration बन सकती है। सुरक्षित तरीका है: पहले state inventory बनवाओ, client state और server state अलग करवाओ, फिर एक छोटा हिस्सा refactor करवाओ।
इस guide में todo, cart, settings और products के examples हैं। साथ में copy-paste React/TypeScript code, tests, सुरक्षित prompts, official links और monetization CTA भी हैं।
पहले state को classify करें
Library चुनने से पहले पूछें: यह data किसका है? क्या यह component UI का local state है, browser में shared client state है, persisted preference है, या server state है?
flowchart TD
A["React state management"] --> B["Local UI state"]
A --> C["Shared client state"]
A --> D["Persisted preferences"]
A --> E["Server state"]
B --> B1["input, modal, tab"]
C --> C1["cart, wizard, editor"]
D --> D1["theme, density, locale"]
E --> E1["products, orders, profile from API"]
Local UI state component के पास रहना चाहिए। Simple value के लिए useState, और कई actions वाली screen के लिए useReducer काफी है। Cart या editor draft जैसे shared client state के लिए Zustand useful हो सकता है। Theme और density जैसे छोटे settings के लिए Jotai अच्छा है। API data server state है, इसलिए TanStack Query बेहतर boundary देता है।
React कब काफी है
React की official Managing State guide state structure, lifting state up, reducer और context का क्रम बताती है। Claude Code को भी यही क्रम follow करवाएं।
| Need | First choice | Library कब सोचें |
|---|---|---|
| Input, tab, modal | useState | लगभग कभी नहीं |
| कई actions वाली screen | useReducer | reducer कई routes में जाए |
| Deep prop passing | Context | updates बहुत frequent हों |
| Cart या draft | Zustand | दूर के components इसे update करें |
| Theme, density | Jotai | कई छोटे preferences हों |
| Products, orders API | TanStack Query | cache, retry, refetch, invalidation चाहिए |
Practical rule: value तीन से कम जगह use होती है, API से नहीं आती, reload के बाद नहीं बचनी चाहिए, तो React built-ins से शुरू करें।
Use case 1: Todo with useReducer and Context
Todo list में add, toggle और delete actions हैं। Server sync नहीं है, इसलिए reducer safe और testable है।
import {
createContext,
useContext,
useMemo,
useReducer,
type Dispatch,
type ReactNode,
} from "react";
export type Todo = {
id: string;
title: string;
done: boolean;
};
type TodoAction =
| { type: "added"; title: string }
| { type: "toggled"; id: string }
| { type: "deleted"; id: string };
export function todoReducer(todos: Todo[], action: TodoAction): Todo[] {
switch (action.type) {
case "added":
return [
...todos,
{ id: crypto.randomUUID(), title: action.title.trim(), done: false },
];
case "toggled":
return todos.map((todo) =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo,
);
case "deleted":
return todos.filter((todo) => todo.id !== action.id);
default:
return todos;
}
}
const TodoStateContext = createContext<Todo[] | null>(null);
const TodoDispatchContext = createContext<Dispatch<TodoAction> | null>(null);
export function TodoProvider({ children }: { children: ReactNode }) {
const [todos, dispatch] = useReducer(todoReducer, []);
const stateValue = useMemo(() => todos, [todos]);
return (
<TodoStateContext.Provider value={stateValue}>
<TodoDispatchContext.Provider value={dispatch}>
{children}
</TodoDispatchContext.Provider>
</TodoStateContext.Provider>
);
}
export function useTodos() {
const value = useContext(TodoStateContext);
if (value === null) throw new Error("useTodos must be used in TodoProvider");
return value;
}
export function useTodoDispatch() {
const value = useContext(TodoDispatchContext);
if (value === null) {
throw new Error("useTodoDispatch must be used in TodoProvider");
}
return value;
}
Claude Code से छोटे बदलाव मांगना आसान है: empty title रोकें, edited action जोड़ें, reducer tests लिखें। Diff भी review करना आसान रहेगा।
Use case 2: Cart with Zustand
Cart product page, header, drawer और checkout में दिखता है। Zustand hook-based store देता है, और persist से safe fields reload के बाद बच सकते हैं।
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
export type CartItem = {
productId: string;
name: string;
price: number;
quantity: number;
};
type CartState = {
items: CartItem[];
currency: "JPY" | "USD";
addItem: (item: Omit<CartItem, "quantity">) => void;
removeItem: (productId: string) => void;
setQuantity: (productId: string, quantity: number) => void;
clear: () => void;
total: () => number;
};
export const useCartStore = create<CartState>()(
persist(
(set, get) => ({
items: [],
currency: "USD",
addItem: (item) =>
set((state) => {
const current = state.items.find(
(entry) => entry.productId === item.productId,
);
if (current) {
return {
items: state.items.map((entry) =>
entry.productId === item.productId
? { ...entry, quantity: entry.quantity + 1 }
: entry,
),
};
}
return { items: [...state.items, { ...item, quantity: 1 }] };
}),
removeItem: (productId) =>
set((state) => ({
items: state.items.filter((item) => item.productId !== productId),
})),
setQuantity: (productId, quantity) =>
set((state) => ({
items: state.items.map((item) =>
item.productId === productId
? { ...item, quantity: Math.max(1, quantity) }
: item,
),
})),
clear: () => set({ items: [] }),
total: () =>
get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}),
{
name: "cart-v1",
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({
items: state.items,
currency: state.currency,
}),
},
),
);
Cart local हो सकता है, लेकिन final price, stock, discount और payment server validate करेगा। Prompt में लिखें कि token या sensitive data persist नहीं करना है।
Use case 3: Settings with Jotai
Theme, density, sidebar जैसे छोटे settings के लिए Jotai atom model useful है। Derived label या CSS class भी atom से निकाली जा सकती है।
import { atom } from "jotai";
import { atomWithStorage } from "jotai/utils";
export const themeAtom = atomWithStorage<"light" | "dark">(
"settings.theme",
"light",
);
export const densityAtom = atomWithStorage<"comfortable" | "compact">(
"settings.density",
"comfortable",
);
export const sidebarOpenAtom = atom(true);
export const settingsLabelAtom = atom((get) => {
const theme = get(themeAtom) === "dark" ? "dark theme" : "light theme";
const density =
get(densityAtom) === "compact" ? "compact layout" : "comfortable layout";
return `${theme}, ${density}`;
});
export const resetSettingsAtom = atom(null, (_get, set) => {
set(themeAtom, "light");
set(densityAtom, "comfortable");
set(sidebarOpenAtom, true);
});
हर चीज atom न बनाएं। Settings और derived values ठीक हैं, लेकिन API cache को TanStack Query में रखें।
Use case 4: Products with TanStack Query
Products, orders और profile server state हैं। वे stale हो सकते हैं, request fail हो सकती है, और दूसरे device से बदल सकते हैं। TanStack Query v5 fetching, caching, syncing और mutation invalidation संभालता है।
import {
QueryClient,
QueryClientProvider,
useMutation,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import type { ReactNode } from "react";
type Product = {
id: string;
name: string;
price: number;
stock: number;
};
const queryClient = new QueryClient();
export function ProductsProvider({ children }: { children: ReactNode }) {
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
}
async function fetchProducts(category: string): Promise<Product[]> {
const response = await fetch(`/api/products?category=${category}`);
if (!response.ok) throw new Error("Failed to fetch products");
return response.json() as Promise<Product[]>;
}
async function addCartLine(productId: string) {
const response = await fetch("/api/cart/lines", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ productId }),
});
if (!response.ok) throw new Error("Failed to add cart line");
return response.json();
}
export function useProducts(category: string) {
return useQuery({
queryKey: ["products", category],
queryFn: () => fetchProducts(category),
staleTime: 60_000,
});
}
export function useAddCartLine() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: addCartLine,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["cart"] });
},
});
}
Boundary साफ रखें: Zustand temporary cart intent रख सकता है; TanStack Query server-confirmed products, cart, orders और checkout status रखेगा।
Claude Code prompts और tests
How Claude Code works context, action और verification loop बताता है। इसी को prompt में रखें।
इस React app के state management को inspect करें। अभी files edit न करें।
Goals:
- client state और server state अलग करें
- जो useState/useReducer/Context में रह सकता है उसे mark करें
- Zustand/Jotai/TanStack Query की जरूरत हो तो reason लिखें
Return:
- state inventory table
- duplicate या derived state
- API data जो client state में रखा गया है
- safe migration order और tests
Pure logic tests पहले लिखें।
import { describe, expect, it, vi } from "vitest";
import { todoReducer, type Todo } from "./TodoProvider";
describe("todoReducer", () => {
it("adds a todo with a generated id", () => {
vi.spyOn(crypto, "randomUUID").mockReturnValue("todo-1");
const result = todoReducer([], { type: "added", title: "Write tests" });
expect(result).toEqual([
{ id: "todo-1", title: "Write tests", done: false },
]);
});
it("toggles a todo", () => {
const initial: Todo[] = [{ id: "todo-1", title: "Ship", done: false }];
const result = todoReducer(initial, { type: "toggled", id: "todo-1" });
expect(result[0].done).toBe(true);
});
});
TanStack Query के लिए official testing guide की तरह हर test में नया QueryClient बनाएं।
Pitfalls, links, CTA
Common pitfalls: सब कुछ global store में डालना, server state को Zustand में रखना, derived value duplicate करना, sensitive data localStorage में रखना, और Claude Code diff को बिना test/build/manual check accept करना।
Official references: React Managing State, Zustand persist, Jotai, TanStack Query overview, Claude Code best practices। Prompting के लिए better prompts guide और Claude Code productivity tips पढ़ें।
Reusable material चाहिए तो products और templates देखें। Team rollout, review rules और real repository migration के लिए training / consultation बेहतर next step है।
Practical result: server state को पहले TanStack Query में ले जाने से सबसे बड़ा फायदा मिला। Zustand छोटा हुआ, Jotai सिर्फ preferences पर रहा, और Claude Code के diffs review करना आसान हुआ क्योंकि पहले state inventory बन चुकी थी।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code permission safety ladder: access धीरे-धीरे बढ़ाएं
read-only से limited edits, proof commands और deploy checks तक permission बढ़ाने की सुरक्षित ladder.
Claude Code Small PR Proof Pack: छोटे PR को review-ready बनाना
Claude Code PR के लिए diff, checks, public URL, CTA path और rollback वाला practical proof pack.
Claude Code Review Gate Before Commit: diff, test, public URL और CTA जांच
Claude Code से commit से पहले review gate बनाएं: diff, build, public URL, Gumroad, consultation, tests और unrelated files।