跨平台状态管理完全指南:React / Next.js / Flutter / Swift / RxSwift / SwiftUI / ArkTS / RxJava / Agera / Jetpack Compose

由浅入深,从基本概念到源码原理,再到实战案例,系统梳理 Web、移动、原生各平台的状态管理方案


一、什么是状态管理?

1.1 状态(State)的本质

状态是应用在某一时刻的数据快照,它决定了 UI 的展示内容和行为。任何会随时间变化的数据——用户输入、网络请求结果、权限、主题——都可视为状态。

1
2
3
4
5
6
7
8
9
+------------+   +------------+   +------------+   +------------+
| User Input | | Biz Data | | UI State | | Server |
| Form/Search| | List/Detail| |Modal/Load | | Cache |
+------+-----+ +------+-----+ +------+-----+ +------+-----+
| | | |
+-----------------+--------+--------+-----------------+
|
v
UI 渲染 / 副作用执行

用户输入·业务数据·UI状态·服务端 → 汇聚为统一的 UI 渲染与副作用执行

1.2 为什么需要状态管理?

痛点 说明 状态管理的价值
散落 状态分散在各处,难以追踪 集中、可预测的数据流
同步 多处 UI 依赖同一数据,容易不一致 单一数据源(Single Source of Truth)
生命周期 状态何时创建、何时销毁、何时持久化 与组件/页面生命周期绑定
跨层级 深层级组件需要访问顶层状态 提供 Context / Provider / 依赖注入
可测试 业务逻辑与 UI 耦合,难以单测 状态逻辑可独立测试

1.3 各平台状态管理方案概览

平台/框架 主要方案 核心机制 特点
React Hooks (useState/useReducer) 虚拟 DOM diff + 依赖收集 函数式、声明式、生态丰富
Next.js Server Components + Client State 服务端/客户端分离 减少客户端 JS、流式渲染
Flutter Provider / Riverpod / Bloc InheritedWidget / Listenable Dart 异步流、可组合
Swift Combine / 手动 KVO 发布-订阅 系统级、与 SwiftUI 整合
RxSwift Observable / Subject 响应式流 操作符丰富、事件驱动
SwiftUI @State / @Binding / @Observable 声明式 + 属性包装器 数据驱动视图、自动重绘
ArkTS @State / @Prop / @Link 装饰器 + 响应式 鸿蒙声明式 UI
RxJava Observable / Subject 响应式流 异步编排、背压支持
Agera Observable / Updatable 推事件-拉数据 Google 早期方案,已归档
Jetpack Compose remember / mutableStateOf 重组(Recomposition) 声明式、与 Kotlin 协程整合

二、状态管理的核心原则与模式

2.1 单向数据流(Unidirectional Data Flow)

大多数现代框架采用「单向数据流」:状态向下流动,事件向上反馈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────┐
│ State │ ← 单一数据源
└──────┬──────┘
│ 只读传递

┌─────────────┐
│ View │ → 用户操作
└──────┬──────┘
│ 事件 / Action

┌─────────────┐
│ Reducer / │ → 计算新状态
│ setState │
└──────┬──────┘

└──────────► 更新 State → 重新渲染

React、Redux、Flutter Bloc、Jetpack Compose 的 ViewModel 均遵循此模式。

2.2 观察者模式:统一的底层基石

状态管理大多基于观察者模式:被观察对象变化时,通知所有订阅者。

平台 被观察者 观察者 通知方式
React useState 返回值 组件函数 调度重渲染
SwiftUI @State / @Published 视图 body 自动重算 body
Jetpack Compose mutableStateOf Composable 重组(Recomposition)
Flutter ChangeNotifier addListener notifyListeners
RxJava Observable Observer onNext
ArkTS @State 变量 组件 build 框架触发重绘

2.3 局部状态 vs 全局状态

类型 范围 典型方案 场景
局部状态 单组件/单页面 useState / @State / remember 输入框、折叠面板、动画
共享状态 多组件/跨页面 Context / Provider / ViewModel 主题、用户信息、购物车
服务端状态 与后端同步 React Query / SWR / 自定义 列表、详情、缓存

三、Web 前端:React 与 Next.js

3.1 React 状态管理

3.1.1 基本概念:useState

useState 是 React 最基础的状态 Hook,返回当前值和更新函数:

1
2
3
4
5
6
7
8
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}

原理简述:React 在内部维护 Fiber 树,每个组件对应一个 Fiber 节点,useState 将状态存储在 Fiber 的 memoizedState 链表中。更新时调度 setState,标记组件需要重渲染,之后执行 diff 并提交 DOM 变更。

3.1.2 派生状态与 useReducer

当状态逻辑复杂时,用 useReducer 集中处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 (
<>
<span>{state.count}</span>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</>
);
}

3.1.3 跨组件共享:Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const ThemeContext = createContext('light');

function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Header />
<Main />
</ThemeContext.Provider>
);
}

function Header() {
const { theme } = useContext(ThemeContext);
return <div className={theme}>...</div>;
}

注意:Context 变化会导致所有消费该 Context 的组件重渲染,可配合 useMemo / 拆分 Provider 优化。

3.1.4 副作用与依赖:useEffect

1
2
3
4
5
6
7
8
9
10
const [keyword, setKeyword] = useState('');
const [users, setUsers] = useState([]);

useEffect(() => {
if (keyword.length < 2) return;
const timer = setTimeout(() => {
fetchUsers(keyword).then(setUsers);
}, 300);
return () => clearTimeout(timer); // 清理函数
}, [keyword]);

3.2 Next.js 状态管理

Next.js 引入 Server ComponentsClient Components 的区分,状态管理策略随之变化。

3.2.1 服务端 vs 客户端状态

类型 组件 可用能力 典型场景
Server Component 默认 直接 fetch、访问 DB、无 hooks 静态内容、SEO、首屏数据
Client Component 'use client' useState、useEffect、事件处理 交互、表单、实时更新

3.2.2 组合模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// app/page.tsx - Server Component(默认)
async function Page() {
const data = await fetchFromDB(); // 服务端直接拉取
return (
<Layout>
<StaticContent data={data} />
<InteractiveCart /> {/* Client Component,内部用 useState */}
</Layout>
);
}

// components/InteractiveCart.tsx
'use client';
export function InteractiveCart() {
const [items, setItems] = useState([]);
return <CartUI items={items} onAdd={...} />;
}

3.2.3 Context 在 Next.js 中的使用

Context Provider 必须放在 Client Component 中:

1
2
3
4
5
6
7
8
9
10
// providers/ThemeProvider.tsx
'use client';
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}

四、跨平台移动:Flutter

4.1 基本概念

Flutter 的 UI 是声明式的:给定状态,构建对应 Widget 树。状态变化触发 setStatenotifyListeners,进而重建 Widget。

4.2 内置方案

4.2.1 StatefulWidget + setState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
int _count = 0;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() => _count++),
child: Text('$_count'),
);
}
}

4.2.2 ValueNotifier + ValueListenableBuilder(局部重建)

1
2
3
4
5
6
7
final counter = ValueNotifier<int>(0);

ValueListenableBuilder<int>(
valueListenable: counter,
builder: (context, value, child) => Text('$value'),
)
// 只有 ValueListenableBuilder 会重建,而非整棵子树

4.2.3 ChangeNotifier + Provider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CartModel extends ChangeNotifier {
final List<Item> _items = [];
void add(Item item) {
_items.add(item);
notifyListeners();
}
}

// 根节点
ChangeNotifierProvider(create: (_) => CartModel(), child: MyApp())

// 子节点消费
context.watch<CartModel>(); // 监听变化并重建
context.read<CartModel>(); // 仅读取,不监听

4.3 进阶:Riverpod / Bloc

  • Riverpod:编译期安全、可测试、不依赖 BuildContext
  • Bloc:事件 → 状态 的显式映射,适合复杂业务流

五、iOS 原生:Swift、RxSwift、SwiftUI

5.1 Swift 传统方式

  • 属性观察器willSet / didSet
  • KVOobserve(_:options:changeHandler:)
  • 通知NotificationCenter
  • Delegate / 闭包回调

5.2 RxSwift 响应式状态

RxSwift 将状态抽象为,通过 Observable / Subject 管理:

1
2
3
4
5
6
7
8
9
10
11
// 文本框输入 → 搜索
let searchResults = searchTextField.rx.text.orEmpty
.debounce(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { api.search($0) }
.observe(on: MainScheduler.instance)

searchResults
.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { _, model, cell in
cell.textLabel?.text = model.name
}
.disposed(by: disposeBag)

Subject 可同时作为观察者和被观察者,常用于「桥接」命令式代码与响应式流:

1
2
3
4
5
6
7
8
let buttonTaps = PublishSubject<Void>()
buttonTaps
.flatMapLatest { api.fetchData() }
.subscribe(onNext: { updateUI($0) })
.disposed(by: disposeBag)

// 命令式触发
button.rx.tap.bind(to: buttonTaps).disposed(by: disposeBag)

5.3 SwiftUI 声明式状态

5.3.1 @State:组件内部值类型状态

1
2
3
4
5
6
struct CounterView: View {
@State private var count = 0
var body: some View {
Button("Count: \(count)") { count += 1 }
}
}

5.3.2 @Binding:父子双向绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
struct ParentView: View {
@State private var isOn = false
var body: some View {
ToggleView(isOn: $isOn) // 传递 Binding
}
}

struct ToggleView: View {
@Binding var isOn: Bool
var body: some View {
Toggle("", isOn: $isOn)
}
}

5.3.3 @ObservedObject / @StateObject:引用类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class UserViewModel: ObservableObject {
@Published var name = ""
@Published var isLoading = false
}

struct ProfileView: View {
@StateObject private var viewModel = UserViewModel() // 创建并持有
var body: some View {
Text(viewModel.name)
}
}

struct ChildView: View {
@ObservedObject var viewModel: UserViewModel // 从父组件传入
var body: some View { ... }
}

5.3.4 @Observable(iOS 17+)

新宏 @Observable 可替代 ObservableObject,更简洁:

1
2
3
4
5
6
7
8
9
10
11
12
@Observable
class User {
var name: String = ""
var age: Int = 0
}

struct UserView: View {
@State private var user = User()
var body: some View {
Text(user.name) // 自动追踪依赖
}
}

六、Android 原生:RxJava、Agera、Jetpack Compose

6.1 RxJava 响应式状态

1
2
3
4
5
6
7
8
9
val querySubject = PublishSubject.create<String>()
val results = querySubject
.debounce(300, TimeUnit.MILLISECONDS)
.filter { it.length >= 2 }
.switchMap { api.search(it).toObservable() }
.observeOn(AndroidSchedulers.mainThread())

results.subscribe { updateUI(it) }.addTo(compositeDisposable)
querySubject.onNext(editText.text.toString())

StateFlow / SharedFlow(Kotlin Flow)是官方推荐替代 LiveData 的方案:

1
2
3
4
5
6
7
8
9
10
class ViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()

fun loadData() {
viewModelScope.launch {
_uiState.value = UiState.Success(repository.fetch())
}
}
}

6.2 Agera 简介(已归档)

Agera 是 Google 早期的轻量级响应式库,采用推事件、拉数据模型:

  • Observable:广播事件
  • Updatable:监听事件,从 Repository 拉取数据
1
2
3
4
5
6
7
8
9
// 概念示例(Agera 已归档,仅作了解)
val repository = Repositories.repositoryWithInitialValue(initialData)
.observe()
.onUpdatesPerLoop()
.getFrom { fetchFromNetwork() }
.compile()

repository.addUpdatable(updatable)
// 事件触发时,Updatable 从 Repository 拉取最新数据

注意:Agera 已于 2023 年 3 月归档,新项目建议使用 Kotlin Flow / StateFlow。

6.3 Jetpack Compose 状态管理

6.3.1 remember + mutableStateOf

1
2
3
4
5
6
7
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
  • remember:在重组间保持值,避免每次重组重新创建
  • mutableStateOf:创建可观察状态,读该状态的 Composable 会在值变化时重组

6.3.2 状态提升(State Hoisting)

1
2
3
4
5
6
7
8
9
10
11
12
@Composable
fun Parent() {
var count by remember { mutableStateOf(0) }
Child(count = count, onCountChange = { count = it })
}

@Composable
fun Child(count: Int, onCountChange: (Int) -> Unit) {
Button(onClick = { onCountChange(count + 1) }) {
Text("$count")
}
}

6.3.3 ViewModel + StateFlow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(MyState())
val state: StateFlow<MyState> = _state.asStateFlow()

fun update() {
_state.update { it.copy(...) }
}
}

@Composable
fun Screen(viewModel: MyViewModel = viewModel()) {
val state by viewModel.state.collectAsStateWithLifecycle()
// 使用 state
}

6.3.4 配置变更保留:rememberSaveable

1
2
var count by rememberSaveable { mutableStateOf(0) }
// 屏幕旋转等配置变更后,count 会保留

七、鸿蒙:ArkTS

7.1 装饰器驱动的状态

ArkTS 通过装饰器声明「可观察」状态,状态变化自动触发 UI 更新。

7.1.1 @State:组件内部状态

1
2
3
4
5
6
7
8
9
10
11
12
@Entry
@Component
struct Counter {
@State count: number = 0
build() {
Column() {
Text(`Count: ${this.count}`)
Button('+1')
.onClick(() => { this.count++ })
}
}
}

7.1.2 @Prop:父 → 子单向

1
2
3
4
5
6
7
8
9
10
@Component
struct Child {
@Prop value: number // 父组件传入,子组件只读
build() {
Text(`${this.value}`)
}
}

// 父组件
Child({ value: this.count })

7.1.3 @Link:父子双向

1
2
3
4
5
6
7
8
9
10
@Component
struct Child {
@Link value: number // 子组件修改会同步回父组件
build() {
Button('+1').onClick(() => { this.value++ })
}
}

// 父组件
Child({ value: $count }) // $ 语法传递引用

7.1.4 @Provide / @Consume:跨层级

1
2
3
4
5
// 祖先
@Provide('theme') theme: string = 'light'

// 任意后代
@Consume('theme') theme: string

7.1.5 @Observed + @ObjectLink:嵌套对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Observed
class User {
name: string
constructor(name: string) { this.name = name }
}

@Component
struct UserView {
@ObjectLink user: User
build() {
Text(this.user.name)
.onClick(() => { this.user.name = 'New' }) // 触发更新
}
}

7.2 状态管理 V2(@ObservedV2 + @Trace)

V2 支持深层属性观察,解决嵌套对象内部属性不可观察的问题。


八、跨平台对比与选型

8.1 概念映射表

概念 React Flutter SwiftUI Jetpack Compose ArkTS
组件内状态 useState State @State remember + mutableStateOf @State
父子单向 props 构造函数参数 普通参数 参数 @Prop
父子双向 回调 + props 回调 @Binding 回调 + 参数 @Link
跨层级 Context Provider/InheritedWidget EnvironmentObject CompositionLocal @Provide/@Consume
引用类型 useRef/Context ChangeNotifier @ObservableObject ViewModel @Observed+@ObjectLink

8.2 选型建议

场景 推荐方案
简单 UI 状态 各平台内置(useState / @State / remember)
跨组件共享 Context / Provider / ViewModel / @Provide
复杂异步流 RxSwift / RxJava / Kotlin Flow
服务端数据 React Query / SWR / ViewModel + Repository
Next.js 全栈 Server Components 拉数据 + Client 管理交互态
新 Android 项目 Jetpack Compose + ViewModel + StateFlow
新 iOS 项目 SwiftUI + @Observable
鸿蒙应用 ArkUI 装饰器体系

九、源码原理浅析

9.1 React:Fiber 与 Hooks 链表

React 在 Fiber 节点上维护 memoizedState 链表,每个 Hook 对应链表中的一个节点:

1
2
3
4
5
Fiber.memoizedState → useState → useEffect → useContext → ...

├─ baseState
├─ baseQueue
└─ next (下一个 Hook)

setState 会将更新放入队列,调度器在合适时机执行重渲染,按顺序应用更新,保证 Hooks 调用顺序稳定。

9.2 Jetpack Compose:快照与重组

Compose 使用 Snapshot 系统追踪状态读取:

  1. mutableStateOf 创建 SnapshotMutableState
  2. 读取 state.value 时,当前 Composable 的「重组作用域」会记录对该 state 的依赖
  3. 写入 state.value = x 时,Compose 标记依赖该 state 的作用域需要重组
  4. 重组时重新执行对应 Composable,得到新 UI
1
读取 state → 记录依赖 → 写入 state → 标记无效 → 调度重组 → 重新执行 Composable

9.3 SwiftUI:@State 与依赖追踪

SwiftUI 在编译期和运行期结合,追踪 body 中对 @State 等属性的访问。当 @State 变化时,视图的 body 会重新求值,生成新的 View 描述,再与旧描述 diff 后更新真实视图。

9.4 ArkTS:装饰器与更新调度

@State 等装饰器在编译期生成观察逻辑,运行时状态变化会标记组件为脏,下一帧统一执行 build 更新 UI,类似 Flutter 的 setState + 帧调度。


十、实战案例

10.1 登录表单校验(多字段组合)

需求:用户名 ≥3 字符、密码 ≥6 字符时,登录按钮才可点击。

React

1
2
3
4
const [user, setUser] = useState('');
const [pwd, setPwd] = useState('');
const canLogin = user.length >= 3 && pwd.length >= 6;
return <Button disabled={!canLogin}>登录</Button>;

SwiftUI

1
2
3
4
@State private var user = ""
@State private var pwd = ""
var canLogin: Bool { user.count >= 3 && pwd.count >= 6 }
Button("登录") { }.disabled(!canLogin)

Jetpack Compose

1
2
3
4
var user by remember { mutableStateOf("") }
var pwd by remember { mutableStateOf("") }
val canLogin = user.length >= 3 && pwd.length >= 6
Button(onClick = {}, enabled = canLogin) { Text("登录") }

ArkTS

1
2
3
4
5
6
@State user: string = ''
@State pwd: string = ''
private get canLogin(): boolean {
return this.user.length >= 3 && this.pwd.length >= 6
}
Button('登录').enabled(this.canLogin)

10.2 搜索防抖 + 取消旧请求

React

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const [keyword, setKeyword] = useState('');
const [results, setResults] = useState([]);

useEffect(() => {
if (keyword.length < 2) return;
const timer = setTimeout(() => {
let cancelled = false;
search(keyword).then(data => {
if (!cancelled) setResults(data);
});
return () => { cancelled = true; };
}, 300);
return () => clearTimeout(timer);
}, [keyword]);

RxSwift

1
2
3
4
5
6
7
searchTextField.rx.text.orEmpty
.filter { $0.count >= 2 }
.debounce(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { api.search($0) }
.observe(on: MainScheduler.instance)
.bind(to: resultsRelay)
.disposed(by: disposeBag)

Jetpack Compose + ViewModel

1
2
3
4
5
6
val query = MutableStateFlow("")
val results = query
.debounce(300)
.filter { it.length >= 2 }
.flatMapLatest { repository.search(it) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

10.3 购物车总价实时计算

Flutter (Provider)

1
2
3
4
5
class CartModel extends ChangeNotifier {
final List<CartItem> _items = [];
double get total => _items.fold(0, (sum, item) => sum + item.price * item.qty);
void add(CartItem item) { _items.add(item); notifyListeners(); }
}

React

1
2
3
4
5
const [items, setItems] = useState([]);
const total = useMemo(
() => items.reduce((s, i) => s + i.price * i.qty, 0),
[items]
);

10.4 Next.js 服务端数据 + 客户端状态

1
2
3
4
5
6
7
8
9
10
// app/product/[id]/page.tsx
export default async function Page({ params }: { params: { id: string } }) {
const product = await fetchProduct(params.id); // 服务端拉取
return (
<div>
<ProductInfo product={product} />
<AddToCartButton productId={product.id} /> {/* 内部用 useState 管理数量 */}
</div>
);
}

十一、总结与最佳实践

11.1 核心要点

维度 共性
本质 状态驱动 UI,变化触发更新
模式 单向数据流、观察者、单一数据源
局部 vs 全局 按范围选择合适的共享机制
生命周期 状态与组件/页面生命周期绑定,避免泄漏

11.2 最佳实践

  1. 最小化状态:能推导的不要存储,用 useMemo / computed / getter
  2. 状态提升:当多组件需要共享时,提升到共同祖先
  3. 不可变更新:避免直接修改,使用 setState/copy/扩展运算符
  4. 副作用清理useEffect 清理、Disposable.disposeviewModelScope
  5. 服务端状态分离:与 UI 状态区分,用专门库(React Query 等)管理
  6. 类型安全:TypeScript、Swift、Kotlin 充分利用类型约束状态结构

11.3 各平台快速对照

平台 局部状态 共享状态 异步/流式
React useState Context / Redux useEffect / React Query
Next.js useState (Client) Context (Client) Server Components + fetch
Flutter State / ValueNotifier Provider / Riverpod Stream / Future
SwiftUI @State @ObservableObject / Environment async/await
RxSwift Observable / Subject 同上 + Subject Observable 链
Compose remember + mutableStateOf ViewModel + StateFlow Flow / LaunchedEffect
ArkTS @State @Provide / @Consume Promise / async

参考资源

React / Flutter 状态管理

由浅入深,从基本概念到原理与源码,再到示例与实际项目应用案例,系统梳理两大主流框架中的状态管理方案


一、状态管理基础概念

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 优化。

2.5 Redux / Redux Toolkit:全局状态管理

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
// store/counterSlice.js
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); // 仅 count 变化时重渲染

2.7 React 状态管理原理浅析

useState 的链表结构

React 内部用链表存储 Hooks。每个 Hook 对应链表中的一个节点,通过 FibermemoizedState 串联。

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
// 1. 定义 Model(继承 ChangeNotifier)
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;

void increment() {
_count++;
notifyListeners(); // 通知监听者
}
}

// 2. 在根节点提供
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: MyApp(),
),
);
}

// 3. 在子组件使用
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
// 1. 定义 Provider
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
}

// 2. 在 runApp 外包一层 ProviderScope
void main() {
runApp(ProviderScope(child: MyApp()));
}

// 3. 在组件中使用(无需 context)
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
// 1. 定义 Event 和 State
abstract class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

class CounterState {
final int count;
CounterState(this.count);
}

// 2. 实现 Bloc
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)));
}
}

// 3. 在 UI 中使用
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 与依赖收集

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)在合适的时机批量处理,触发 rendercommit

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(); // 触发 Consumer 等重建
}
}

ProviderInheritedProvideraddListenerChangeNotifier,当 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
// store/cartStore.js
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' }
)
);

// Header 中只订阅 totalCount,避免整 store 变化导致重渲染
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
// providers/auth_provider.dart
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);
}
}

// 路由守卫:根据 authState 跳转登录页或首页
ref.listen(authStateProvider, (prev, next) {
next.whenData((user) {
if (user == null) navigator.pushReplacement(LoginRoute());
});
});

6.3 案例三:列表筛选与分页(React + Redux Toolkit + RTK Query)

需求:筛选条件、分页、缓存、乐观更新。

1
2
3
4
5
6
7
8
// 使用 RTK Query 管理服务端状态
const { data, isLoading, refetch } = useGetProductsQuery({
page: currentPage,
category: selectedCategory,
});

// 本地筛选状态用 Redux 或 useState 均可
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
// 使用 MultiProvider 组合
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 ToolkitZustand,服务端状态用 React Query 等。
  • Flutter:从 setState 起步,共享状态用 Provider 入门,进阶用 RiverpodBloc
  • 选型时关注:团队熟悉度项目规模可测试性与框架生态的契合度

由浅入深掌握上述方案后,可以根据具体业务灵活组合,构建可维护、可扩展的状态管理体系。