Yükleniyor...
Yükleniyor...
Templately runs on Redux Toolkit. TubeOnAI is React Query for server state and a small Zustand store for UI. sapan.dev gets by on Redux Toolkit + URL state. Three projects, three different state strategies, and the pattern that emerged for picking between them.
In the past two years I have shipped React apps that ended up using three different state strategies. Templately is on Redux Toolkit because the team adopted it early and the patterns are deeply baked in. TubeOnAI runs React Query for server state and a small Zustand store for transient UI (player state, modal flags). sapan.dev gets by entirely on Redux Toolkit + URL state, no React Query at all. Below is the pattern that emerged across them — and the things I would not do twice.
The state management conversation in the React ecosystem has changed dramatically. Context API closed the gap for simple cases, React Query and SWR own server state, and the remaining client state problem is served by a range of lean, focused libraries.
Before comparing libraries, the most impactful architectural decision is separating server state from client state. Server state (remote data, async operations) belongs in React Query, SWR, or RTK Query. Client state (UI state, user preferences, shopping cart) belongs in a state manager.
Bilgi
If you are using Redux to store API responses and manage loading states, migrate to React Query first. You may find that very little Redux remains afterward.
Zustand is a 1KB state manager built around a single hook. You define a store as a function that returns state and actions — no reducers, no action types, no boilerplate.
import { create } from 'zustand';
type CartStore = {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
total: () => number;
};
const useCartStore = create<CartStore>((set, get) => ({
items: [],
addItem: (item) =>
set((state) => ({ items: [...state.items, item] })),
removeItem: (id) =>
set((state) => ({ items: state.items.filter((i) => i.id !== id) })),
total: () => get().items.reduce((sum, item) => sum + item.price, 0),
}));
// Usage
function CartSummary() {
const total = useCartStore((state) => state.total());
return <span>Total: ${total}</span>;
}Jotai takes a different approach — rather than a single store, you compose state from atoms. Components subscribe only to the atoms they use, minimizing re-renders. This makes it excellent for complex UIs with many independent pieces of state.
import { atom, useAtom, useAtomValue } from 'jotai';
const countAtom = atom(0);
const doubledAtom = atom((get) => get(countAtom) * 2);
// Derived async atom
const userAtom = atom(async (get) => {
const id = get(userIdAtom);
return fetch(`/api/users/${id}`).then((r) => r.json());
});
function Counter() {
const [count, setCount] = useAtom(countAtom);
const doubled = useAtomValue(doubledAtom);
return (
<div>
<button onClick={() => setCount((c) => c + 1)}>{count}</button>
<span>Doubled: {doubled}</span>
</div>
);
}Redux Toolkit (RTK) remains the right choice for large applications with complex state interactions, time-travel debugging requirements, or existing Redux codebases. RTK Query in particular is an excellent solution for API state management.
İpucu
Start with Zustand. It has the lowest learning curve, the smallest bundle, and can scale further than most apps ever need. Switch to RTK if you genuinely need devtools, middleware, or the Redux ecosystem.
Across Templately, TubeOnAI, and sapan.dev, the lesson that surfaced repeatedly: separate server state from client state first, then pick the smallest library that fits the remaining client state. Most projects I have started in the past year reach for React Query plus a tiny Zustand store, and the team rarely outgrows that combination. Redux still earns its keep when the state machine is genuinely complex, the team is large, or you need the time-travel debugging — but those are increasingly the exceptions, not the rule. The best state manager is the one your team can read at a glance six months from now.
React kategorisinde daha fazlası
Notes from running the React Compiler on the BetterDocs admin (a 4-year-old codebase with ~80 components and useMemo scattered everywhere) and starting clean with it on xCloud v1. What broke, what did not, and where I still reach for manual memoization.
On the sapan.dev contact form I rewrote the React Hook Form + manual pending/error setup as a single useActionState call. The component dropped from ~80 lines to ~30, and useOptimistic gave the submit button a snappier feel. Notes on what Actions actually replace.
sapan.dev is built server-component-first across all 16 locales. Notes from designing it that way: the bundle savings, the patterns I keep reaching for, and the moments I had to pull back from "everything server" because the UX needed it.