由浅入深,从基本概念到原理与源码,再到示例与实际项目应用案例,系统梳理两大主流框架中的状态管理方案
一、状态管理基础概念
1.1 什么是状态(State)?
状态是驱动 UI 变化的数据。当状态改变时,界面随之更新,形成「数据驱动视图」的声明式模式。
1 2 3 4
| 状态 ──► 视图 ▲ │ │ ▼ └── 用户交互 / 网络请求 / 定时器等
|
1.2 状态的分类
| 类型 |
作用域 |
典型场景 |
生命周期 |
| 本地状态 |
单组件 |
输入框内容、展开/折叠、选中项 |
跟随组件 |
| 共享状态 |
多组件 |
用户信息、主题、购物车 |
需要提升或全局管理 |
| 服务端状态 |
与后端同步 |
API 数据、缓存 |
异步、需缓存策略 |
1.3 为什么需要状态管理?
随着应用复杂度上升,会出现:
- 状态提升导致 props 层层传递(prop drilling)
- 状态分散导致难以追踪和调试
- 重复请求、缓存失效等数据一致性问题
状态管理方案的目标:集中、可预测、易维护。
二、React 状态管理
2.1 内置方案概览
| 方案 |
适用场景 |
特点 |
useState |
本地状态 |
简单、轻量 |
useReducer |
复杂本地状态 |
可预测、易测试 |
Context API |
跨层级共享 |
官方内置、易造成不必要的重渲染 |
2.2 useState:最简单的本地状态
1 2 3 4 5 6 7 8 9 10 11 12
| function Counter() { const [count, setCount] = useState(0);
return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>+1</button> {/* 函数式更新,避免闭包陷阱 */} <button onClick={() => setCount(prev => prev + 1)}>+1 (安全)</button> </div> ); }
|
惰性初始化:初始值可以是函数,仅在首次渲染执行。
1
| const [state, setState] = useState(() => expensiveComputation());
|
2.3 useReducer:复杂状态的 reducer 模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; } }
function Counter() { const [state, dispatch] = useReducer(reducer, { count: 0 });
return ( <div> <span>{state.count}</span> <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </div> ); }
|
2.4 Context API:跨层级共享状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const ThemeContext = createContext('light');
function App() { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Page /> </ThemeContext.Provider> ); }
function Page() { const { theme } = useContext(ThemeContext); return <div className={theme}>...</div>; }
|
注意:Provider 的 value 变化会导致所有 useContext 的消费者重渲染,需配合 useMemo 或拆分 Context 优化。
Redux 采用单向数据流:View → Action → Reducer → Store → View。
1 2 3 4 5
| ┌─────────┐ dispatch ┌─────────┐ reduce ┌────────┐ │ View │ ───────────► │ Action │ ─────────► │ Store │ └─────────┘ └─────────┘ └────────┘ ▲ │ └──────────────── subscribe ─────────────────────┘
|
Redux Toolkit 示例(官方推荐写法):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: state => { state.value += 1; }, decrement: state => { state.value -= 1; }, incrementByAmount: (state, action) => { state.value += action.payload; }, }, });
export const { increment, decrement, incrementByAmount } = counterSlice.actions; export default counterSlice.reducer;
|
1 2 3 4 5 6 7 8 9 10
| import { useDispatch, useSelector } from 'react-redux'; import { increment } from './store/counterSlice';
function Counter() { const count = useSelector(state => state.counter.value); const dispatch = useDispatch();
return <button onClick={() => dispatch(increment())}>{count}</button>; }
|
2.6 Zustand:轻量级全局状态
Zustand 基于 Hooks,API 简洁,无 Provider 包裹。
1 2 3 4 5 6 7 8 9 10 11 12
| import { create } from 'zustand';
const useStore = create((set) => ({ count: 0, increment: () => set(state => ({ count: state.count + 1 })), decrement: () => set(state => ({ count: state.count - 1 })), }));
function Counter() { const { count, increment } = useStore(); return <button onClick={increment}>{count}</button>; }
|
选择器优化:只订阅需要的字段,避免无关更新。
1
| const count = useStore(state => state.count);
|
2.7 React 状态管理原理浅析
useState 的链表结构
React 内部用链表存储 Hooks。每个 Hook 对应链表中的一个节点,通过 Fiber 的 memoizedState 串联。
1 2 3
| Fiber.memoizedState → Hook1 → Hook2 → Hook3 → ... │ [state, setState]
|
这就是为什么 Hooks 必须在顶层调用、不能放在条件/循环中:链表顺序必须稳定。
setState 的批处理(Batching)
React 18 默认对所有更新进行自动批处理,多次 setState 会合并为一次渲染。
1 2 3 4 5
| function handleClick() { setCount(c => c + 1); setFlag(f => !f); }
|
三、Flutter 状态管理
3.1 方案概览
| 方案 |
官方/社区 |
适用场景 |
特点 |
setState |
内置 |
本地状态 |
简单,整组件重建 |
InheritedWidget |
内置 |
跨层级共享 |
底层基础,一般不直接使用 |
| Provider |
官方推荐 |
中小型应用 |
基于 InheritedWidget,易上手 |
| Riverpod |
社区主流 |
中大型应用 |
编译期安全、可测试、无 context |
| Bloc |
社区 |
复杂业务逻辑 |
事件驱动、可预测、适合团队 |
| GetX |
社区 |
快速开发 |
全能型,状态+路由+依赖注入 |
3.2 setState:最简单的本地状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Counter extends StatefulWidget { @override _CounterState createState() => _CounterState(); }
class _CounterState extends State<Counter> { int _count = 0;
void _increment() { setState(() { _count++; }); }
@override Widget build(BuildContext context) { return Column( children: [ Text('$_count'), ElevatedButton(onPressed: _increment, child: Text('+1')), ], ); } }
|
原理:setState 会标记当前 Element 为脏,在下一帧触发 build 重建子树。
3.3 Provider:官方推荐方案
Provider 基于 InheritedWidget,通过 context.watch<T>() 监听变化并重建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class CounterModel extends ChangeNotifier { int _count = 0; int get count => _count;
void increment() { _count++; notifyListeners(); } }
void main() { runApp( ChangeNotifierProvider( create: (_) => CounterModel(), child: MyApp(), ), ); }
class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { final counter = context.watch<CounterModel>(); return Text('${counter.count}'); } }
|
多种 Provider 类型:
| 类型 |
用途 |
Provider |
不可变值 |
ChangeNotifierProvider |
可变、需 notifyListeners |
FutureProvider |
异步数据 |
StreamProvider |
流数据 |
MultiProvider |
组合多个 Provider |
3.4 Riverpod:下一代状态管理
Riverpod 无 BuildContext 依赖,支持编译期类型安全、易于测试和复用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) { return CounterNotifier(); });
class CounterNotifier extends StateNotifier<int> { CounterNotifier() : super(0); void increment() => state++; }
void main() { runApp(ProviderScope(child: MyApp())); }
class CounterPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(counterProvider); return ElevatedButton( onPressed: () => ref.read(counterProvider.notifier).increment(), child: Text('$count'), ); } }
|
ref 的三大方法:
| 方法 |
作用 |
ref.watch() |
监听变化,值变化时重建 |
ref.read() |
一次性读取,不监听 |
ref.listen() |
监听变化并执行副作用,不重建 |
3.5 Bloc:事件驱动架构
Bloc 将 UI 与业务逻辑解耦,通过 Event → Bloc → State 的流程管理状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| abstract class CounterEvent {} class Increment extends CounterEvent {} class Decrement extends CounterEvent {}
class CounterState { final int count; CounterState(this.count); }
class CounterBloc extends Bloc<CounterEvent, CounterState> { CounterBloc() : super(CounterState(0)) { on<Increment>((event, emit) => emit(CounterState(state.count + 1))); on<Decrement>((event, emit) => emit(CounterState(state.count - 1))); } }
BlocProvider( create: (_) => CounterBloc(), child: BlocBuilder<CounterBloc, CounterState>( builder: (context, state) { return Text('${state.count}'); }, ), )
|
3.6 Flutter 状态管理原理浅析
setState 与 Element 树
1 2 3 4 5 6 7 8 9 10
| setState() 被调用 │ ▼ 标记 Element 为 dirty │ ▼ 下一帧 SchedulerBinding 触发 build │ ▼ Element.rebuild() → State.build()
|
InheritedWidget 通过 context.dependOnInheritedWidgetOfExactType<T>() 建立「依赖关系」。当 InheritedWidget 更新时,依赖它的 Element 会被标记为脏并重建。
Provider 的 notifyListeners() 会触发 InheritedWidget 的更新,从而通知所有 context.watch 的消费者。
四、React vs Flutter 状态管理对比
4.1 概念映射
| 概念 |
React |
Flutter |
| 本地状态 |
useState |
setState |
| 复杂本地状态 |
useReducer |
自建 StatefulWidget + 内部逻辑 |
| 跨层级共享 |
Context |
InheritedWidget / Provider |
| 全局 Store |
Redux / Zustand |
Provider / Riverpod / Bloc |
| 选择器/按需订阅 |
useSelector / useStore(selector) |
context.select / ref.watch(provider.select()) |
4.2 设计哲学差异
| 维度 |
React |
Flutter |
| 更新粒度 |
组件级,虚拟 DOM diff |
Widget 树重建,Element 复用 |
| 数据流 |
单向(Redux)或自由(Zustand) |
多为单向,Bloc 强调事件流 |
| 依赖注入 |
通过 props / Context |
通过 context / ref(Riverpod) |
| 服务端状态 |
React Query / SWR 等 |
Riverpod 的 FutureProvider、flutter_bloc 等 |
五、源码层面的理解
5.1 React useState 的调度
React 的 setState 会调用 dispatchSetState,将更新放入 updateQueue,由调度器(Scheduler)在合适的时机批量处理,触发 render 和 commit。
1 2 3 4 5 6
| setState(newState) → enqueueUpdate(fiber, update) → scheduleUpdateOnFiber(fiber) → performConcurrentWorkOnRoot / performSyncWorkOnRoot → commitRoot
|
5.2 Flutter ChangeNotifier 与 Listenable
ChangeNotifier 继承 Listenable,内部维护 _listeners 列表。notifyListeners() 遍历并调用所有监听者。
1 2 3 4 5 6
| void notifyListeners() { for (final listener in _listeners) { listener(); } }
|
Provider 的 InheritedProvider 会 addListener 到 ChangeNotifier,当 notifyListeners 被调用时,触发自身 updateShouldNotify 并重建子树。
六、实际项目应用案例
6.1 案例一:电商购物车(React + Zustand)
需求:跨页面购物车数量、增删改、持久化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import { create } from 'zustand'; import { persist } from 'zustand/middleware';
export const useCartStore = create( persist( (set) => ({ items: [], addItem: (product, qty = 1) => set((state) => ({ items: state.items.some((i) => i.id === product.id) ? state.items.map((i) => i.id === product.id ? { ...i, qty: i.qty + qty } : i ) : [...state.items, { ...product, qty }], })), removeItem: (id) => set((state) => ({ items: state.items.filter((i) => i.id !== id) })), totalCount: (state) => state.items.reduce((sum, i) => sum + i.qty, 0), }), { name: 'cart-storage' } ) );
const totalCount = useCartStore((s) => s.items.reduce((sum, i) => sum + i.qty, 0) );
|
6.2 案例二:用户认证流(Flutter + Riverpod)
需求:登录态、token 刷新、路由守卫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| final authStateProvider = StateNotifierProvider<AuthNotifier, AsyncValue<User?>>((ref) { return AuthNotifier(ref); });
class AuthNotifier extends StateNotifier<AsyncValue<User?>> { AuthNotifier(this.ref) : super(const AsyncValue.loading()) { _init(); } final Ref ref;
Future<void> _init() async { final token = await storage.getToken(); if (token == null) { state = const AsyncValue.data(null); return; } state = const AsyncValue.loading(); state = await AsyncValue.guard(() => api.getCurrentUser()); }
Future<void> login(String email, String pwd) async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => api.login(email, pwd)); }
void logout() { storage.clearToken(); state = const AsyncValue.data(null); } }
ref.listen(authStateProvider, (prev, next) { next.whenData((user) { if (user == null) navigator.pushReplacement(LoginRoute()); }); });
|
需求:筛选条件、分页、缓存、乐观更新。
1 2 3 4 5 6 7 8
| const { data, isLoading, refetch } = useGetProductsQuery({ page: currentPage, category: selectedCategory, });
const [filters, setFilters] = useState({ category: '', sort: 'default' });
|
6.4 案例四:主题与多语言(Flutter + Provider)
需求:亮/暗主题、中英文切换,全局生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => ThemeModel()), ChangeNotifierProvider(create: (_) => LocaleModel()), ], child: MyApp(), ), );
final theme = context.watch<ThemeModel>(); final locale = context.watch<LocaleModel>();
|
七、选型建议
| 场景 |
React 推荐 |
Flutter 推荐 |
| 小项目/原型 |
useState + Context |
setState + Provider |
| 中大型项目 |
Redux Toolkit / Zustand |
Riverpod / Bloc |
| 强类型、可测试 |
Redux + TypeScript / Zustand |
Riverpod |
| 复杂业务流、事件驱动 |
Redux / XState |
Bloc |
| 服务端状态 |
React Query / SWR |
Riverpod FutureProvider / dio + 自封装 |
八、总结
- React:从
useState 起步,全局状态优先考虑 Redux Toolkit 或 Zustand,服务端状态用 React Query 等。
- Flutter:从
setState 起步,共享状态用 Provider 入门,进阶用 Riverpod 或 Bloc。
- 选型时关注:团队熟悉度、项目规模、可测试性、与框架生态的契合度。
由浅入深掌握上述方案后,可以根据具体业务灵活组合,构建可维护、可扩展的状态管理体系。