1. 上下文API
import { createContext, useContext, useState } from 'react';
// 1. Create context with type
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);
// 2. Custom hook (type-safe)
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// 3. Provider component
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 4. Usage
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={theme}>
<button onClick={toggleTheme}>Toggle: {theme}</button>
</header>
);
}
// App
function App() {
return (
<ThemeProvider>
<Header />
<Main />
</ThemeProvider>
);
}
2. Zustand——全局狀態
npm install zustand
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
// Define store
interface CartStore {
items: CartItem[];
total: number;
addItem: (item: CartItem) => void;
removeItem: (id: number) => void;
clearCart: () => void;
}
const useCartStore = create<CartStore>()(
devtools(
persist(
(set, get) => ({
items: [],
total: 0,
addItem: (item) => set((state) => ({
items: [...state.items, item],
total: state.total + item.price,
})),
removeItem: (id) => set((state) => {
const item = state.items.find(i => i.id === id);
return {
items: state.items.filter(i => i.id !== id),
total: state.total - (item?.price || 0),
};
}),
clearCart: () => set({ items: [], total: 0 }),
}),
{ name: 'cart-storage' } // localStorage key
)
)
);
// Usage — component tự động re-render khi state thay đổi
function CartBadge() {
const itemCount = useCartStore(state => state.items.length); // Selector
return <span>{itemCount}</span>;
}
function CartTotal() {
const total = useCartStore(state => state.total);
return <span>${total}</span>;
}
function AddButton({ product }: { product: Product }) {
const addItem = useCartStore(state => state.addItem);
return <button onClick={() => addItem(product)}>Add to Cart</button>;
}
3. TanStack查詢-伺服器狀態
npm install @tanstack/react-query
import { QueryClient, QueryClientProvider, useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Setup
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 min
gcTime: 10 * 60 * 1000, // 10 min
retry: 3,
refetchOnWindowFocus: false,
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<MyApp />
</QueryClientProvider>
);
}
// Fetch data
function UserList() {
const { data: users, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: async () => {
const res = await fetch('/api/users');
if (!res.ok) throw new Error('Failed to fetch');
return res.json() as Promise<User[]>;
},
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{users?.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
// Mutation
function CreateUser() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (newUser: CreateUserInput) => {
const res = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser),
});
return res.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] }); // Refetch
},
});
return (
<button
onClick={() => mutation.mutate({ name: 'New User', email: '[email protected]' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Creating...' : 'Create User'}
</button>
);
}
4. 比較狀態管理
| 圖書館 | 類型 | 捆綁包 | 學習 | 最適合 |
|---|---|---|---|---|
| 背景 | 內建 | 0KB | 簡單 | 主題、身份驗證、簡單全域狀態 |
| 祖斯坦 | 客戶端狀態 | 〜1KB | 簡單 | 全球客戶狀態,任何規模 |
| Redux 工具包 | 客戶端狀態 | 〜11KB | 中等 | 大型應用程序,複雜的狀態 |
| 佐泰 | 原子性 | 〜2KB | 簡單 | 原子態,導出值 |
| TanStack查詢 | 伺服器狀態 | 〜12KB | 中等 | API資料、快取、同步 |
策略:使用 Zustand 取得客戶端狀態 + TanStack Query 取得伺服器狀態。上下文僅適用於簡單的主題/身份驗證。
下一篇: 反應模式和性能 — HOC、複合組件、React.memo 和效能最佳化。