# 02-React状态管理 ## 📋 学习目标 - 掌握Context API状态共享 - 学习Redux Toolkit使用 - 理解Zustand轻量状态库 - 掌握状态管理最佳实践 ## 🎯 Context API ### 基本用法 ```jsx import {createContext, useContext, useState} from 'react'; // 创建Context const ThemeContext = createContext(null); // Provider组件 export function ThemeProvider({children}) { const [theme, setTheme] = useState('light'); const toggleTheme = () => { setTheme(prev => prev === 'light' ? 'dark' : 'light'); }; return ( {children} ); } // 自定义Hook export function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error('useTheme must be used within ThemeProvider'); } return context; } // 使用 function App() { return (
); } function Header() { const {theme, toggleTheme} = useTheme(); return (
); } ``` ### 复杂状态管理 ```jsx import {createContext, useContext, useReducer} from 'react'; // 定义状态和Actions const initialState = { user: null, isAuthenticated: false, loading: false, error: null }; function authReducer(state, action) { switch (action.type) { case 'LOGIN_START': return {...state, loading: true, error: null}; case 'LOGIN_SUCCESS': return { ...state, loading: false, isAuthenticated: true, user: action.payload }; case 'LOGIN_ERROR': return { ...state, loading: false, error: action.payload }; case 'LOGOUT': return initialState; default: return state; } } // Context const AuthContext = createContext(null); // Provider export function AuthProvider({children}) { const [state, dispatch] = useReducer(authReducer, initialState); const login = async (credentials) => { dispatch({type: 'LOGIN_START'}); try { const user = await api.login(credentials); dispatch({type: 'LOGIN_SUCCESS', payload: user}); } catch (error) { dispatch({type: 'LOGIN_ERROR', payload: error.message}); } }; const logout = () => { dispatch({type: 'LOGOUT'}); }; return ( {children} ); } // Hook export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within AuthProvider'); } return context; } ``` ## 🔴 Redux Toolkit ### 安装和配置 ```bash pnpm add @reduxjs/toolkit react-redux ``` ### 创建Store ```javascript // store.js import {configureStore} from '@reduxjs/toolkit'; import counterReducer from './features/counter/counterSlice'; import userReducer from './features/user/userSlice'; export const store = configureStore({ reducer: { counter: counterReducer, user: userReducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }) }); export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; ``` ### 创建Slice ```javascript // counterSlice.js import {createSlice, PayloadAction} from '@reduxjs/toolkit'; interface CounterState { value: number; status: 'idle' | 'loading'; } const initialState: CounterState = { value: 0, status: 'idle' }; export const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, incrementByAmount: (state, action: PayloadAction) => { state.value += action.payload; }, reset: (state) => { state.value = 0; } } }); export const {increment, decrement, incrementByAmount, reset} = counterSlice.actions; export default counterSlice.reducer; ``` ### 异步Thunk ```javascript import {createSlice, createAsyncThunk} from '@reduxjs/toolkit'; // 异步Thunk export const fetchUser = createAsyncThunk( 'user/fetchUser', async (userId: string) => { const response = await fetch(`/api/users/${userId}`); return response.json(); } ); interface UserState { data: User | null; loading: boolean; error: string | null; } const initialState: UserState = { data: null, loading: false, error: null }; const userSlice = createSlice({ name: 'user', initialState, reducers: { clearUser: (state) => { state.data = null; } }, extraReducers: (builder) => { builder .addCase(fetchUser.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchUser.fulfilled, (state, action) => { state.loading = false; state.data = action.payload; }) .addCase(fetchUser.rejected, (state, action) => { state.loading = false; state.error = action.error.message || 'Failed to fetch'; }); } }); export const {clearUser} = userSlice.actions; export default userSlice.reducer; ``` ### 使用Redux ```jsx // App.tsx import {Provider} from 'react-redux'; import {store} from './store'; function App() { return ( ); } // Counter.tsx import {useSelector, useDispatch} from 'react-redux'; import {increment, decrement, incrementByAmount} from './counterSlice'; import type {RootState, AppDispatch} from './store'; function Counter() { const count = useSelector((state: RootState) => state.counter.value); const dispatch = useDispatch(); return (

Count: {count}

); } // 带异步的组件 function UserProfile({userId}) { const {data, loading, error} = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); useEffect(() => { dispatch(fetchUser(userId)); }, [userId, dispatch]); if (loading) return
Loading...
; if (error) return
Error: {error}
; return
{data?.name}
; } ``` ### 类型安全的Hooks ```typescript // hooks.ts import {useDispatch, useSelector, TypedUseSelectorHook} from 'react-redux'; import type {RootState, AppDispatch} from './store'; export const useAppDispatch = () => useDispatch(); export const useAppSelector: TypedUseSelectorHook = useSelector; // 使用 import {useAppDispatch, useAppSelector} from './hooks'; function Component() { const count = useAppSelector(state => state.counter.value); const dispatch = useAppDispatch(); // ... } ``` ## 🐻 Zustand ### 基本用法 ```tsx import {create} from 'zustand'; // 创建Store interface CounterStore { count: number; increment: () => void; decrement: () => void; reset: () => void; } export const useCounterStore = create((set) => ({ count: 0, increment: () => set((state) => ({count: state.count + 1})), decrement: () => set((state) => ({count: state.count - 1})), reset: () => set({count: 0}) })); // 使用 function Counter() { const {count, increment, decrement, reset} = useCounterStore(); return (

Count: {count}

); } // 选择性订阅(性能优化) function Display() { const count = useCounterStore(state => state.count); // 只有count变化时才重渲染 return
{count}
; } ``` ### 异步Actions ```javascript interface UserStore { user: User | null; loading: boolean; error: string | null; fetchUser: (id: string) => Promise; } export const useUserStore = create((set) => ({ user: null, loading: false, error: null, fetchUser: async (id) => { set({loading: true, error: null}); try { const response = await fetch(`/api/users/${id}`); const user = await response.json(); set({user, loading: false}); } catch (error) { set({error: error.message, loading: false}); } } })); ``` ### 中间件 ```javascript import {create} from 'zustand'; import {persist, devtools} from 'zustand/middleware'; // 持久化 export const useStore = create( persist( (set) => ({ count: 0, increment: () => set((state) => ({count: state.count + 1})) }), { name: 'counter-storage' // localStorage key } ) ); // DevTools export const useStore = create( devtools( (set) => ({ count: 0, increment: () => set((state) => ({count: state.count + 1})) }), {name: 'CounterStore'} ) ); // 组合中间件 export const useStore = create( devtools( persist( (set) => ({ user: null, setUser: (user) => set({user}) }), {name: 'user-storage'} ), {name: 'UserStore'} ) ); ``` ### Immer集成 ```javascript import {create} from 'zustand'; import {immer} from 'zustand/middleware/immer'; export const useStore = create( immer((set) => ({ todos: [], addTodo: (text) => set((state) => { state.todos.push({id: Date.now(), text, done: false}); }), toggleTodo: (id) => set((state) => { const todo = state.todos.find(t => t.id === id); if (todo) todo.done = !todo.done; }) })) ); ``` ## 🎯 状态管理对比 ### 何时使用Context - 主题、语言等全局配置 - 用户认证状态 - 简单的状态共享 - 不频繁更新的数据 ### 何时使用Redux - 复杂的状态逻辑 - 需要时间旅行调试 - 多个组件需要相同数据 - 需要中间件(如logger、saga) ### 何时使用Zustand - 中小型应用 - 需要简洁的API - 性能要求高 - 不需要复杂的调试工具 ## 💡 最佳实践 ### 1. 状态设计原则 ```typescript // ✅ 扁平化状态 interface State { users: {[id: string]: User}; posts: {[id: string]: Post}; currentUserId: string | null; } // ❌ 深层嵌套 interface State { user: { profile: { settings: { theme: string; } } } } ``` ### 2. 拆分状态 ```typescript // 按功能拆分 const useAuthStore = create(...); const useCartStore = create(...); const useUIStore = create(...); // 而不是 const useGlobalStore = create(...); // 包含所有状态 ``` ### 3. 避免prop drilling ```jsx // ❌ Prop drilling // ✅ 使用Context/状态管理 ``` ### 4. 性能优化 ```jsx // Zustand选择性订阅 const count = useStore(state => state.count); // 只订阅count // Redux使用Reselect import {createSelector} from '@reduxjs/toolkit'; const selectTodos = (state) => state.todos; const selectFilter = (state) => state.filter; const selectFilteredTodos = createSelector( [selectTodos, selectFilter], (todos, filter) => todos.filter(todo => { if (filter === 'all') return true; if (filter === 'active') return !todo.done; if (filter === 'completed') return todo.done; }) ); ``` ## 📚 实践练习 ### 练习1:Todo应用 使用Zustand实现: - 添加、删除、切换Todo - 过滤Todo(全部、已完成、未完成) - 持久化到localStorage ### 练习2:购物车 使用Redux Toolkit实现: - 添加商品到购物车 - 修改数量 - 计算总价 - 异步获取商品列表 ### 练习3:多主题系统 使用Context实现: - 切换主题 - 保存用户偏好 - 支持自定义主题 ## 📚 参考资料 - [Redux Toolkit官方文档](https://redux-toolkit.js.org/) - [Zustand文档](https://docs.pmnd.rs/zustand/getting-started/introduction) - [React Context文档](https://react.dev/reference/react/createContext)