在 React Native 项目中,状态管理几乎是绕不开的话题。Redux 足够成熟,但样板代码偏重;Context 灵活,却容易失控。本文结合真实 RN 项目经验,系统总结 Zustand 在 React Native 中的工程化使用方式,帮助你在复杂度与开发效率之间找到平衡点。
一、为什么在 React Native 中选择 Zustand
在 RN 工程中,引入状态管理通常会面临几个现实问题:
- 项目可能是 Hybrid 架构,RN 只是一个子模块
- 原生工程结构复杂,不希望引入侵入式方案
- 状态类型混杂:UI 状态 + 业务状态 + 缓存状态
- 对性能和渲染稳定性要求较高
Zustand 的优势,正好命中这些痛点:
1. 无 Provider,天然适合 RN Module
tsx
// 不需要 <Provider />
const count = useCounterStore((s) => s.count);
- 不依赖 React Context
- RN 作为子工程接入成本极低
- 非常适合 RN Library / 子业务模块
2. API 极简,降低心智负担
ts
const useStore = create((set) => ({
count: 0,
inc: () => set((s) => ({ count: s.count + 1 })),
}));
没有 reducer、action、slice 的概念,状态即逻辑。
3. 天然支持异步,符合 RN 直觉
ts
fetchList: async () => {
set({ loading: true });
const data = await request();
set({ list: data, loading: false });
}
无需 thunk / saga,减少工程复杂度。
二、Zustand 的核心设计理念
在工程实践中,可以将 Zustand 的理念总结为三点:
1️⃣ Store 是"模块状态容器",不是全局垃圾桶
2️⃣ Selector 是性能优化的核心
3️⃣ 状态拆分优于状态归一化
如果忽略这三点,Zustand 也会被用"烂"。
三、React Native 项目中的推荐目录结构
text
src/
├── store/
│ ├── auth/
│ │ └── useAuthStore.ts
│ ├── ui/
│ │ └── useUIStore.ts
│ ├── list/
│ │ └── useListStore.ts
│ └── index.ts
├── pages/
├── components/
└── services/
设计原则
- 按领域拆 store,而不是按技术拆
- 一个 store 只解决一个问题
- 不搞
rootStore
四、基础 Store 的工程化写法
登录态 Store 示例
ts
type User = {
id: string;
name: string;
};
type AuthState = {
user?: User;
token?: string;
login: (user: User, token: string) => void;
logout: () => void;
};
export const useAuthStore = create<AuthState>((set) => ({
user: undefined,
token: undefined,
login: (user, token) => set({ user, token }),
logout: () => set({ user: undefined, token: undefined }),
}));
使用方式(关键)
ts
const user = useAuthStore((s) => s.user);
const logout = useAuthStore((s) => s.logout);
❗永远不要直接解构整个 store
五、性能优化:RN 中必须重视的问题
1. 禁止全量订阅
❌ 错误示例:
ts
const store = useAuthStore();
这会导致 任何状态变化都触发组件重渲染。
2. Selector + shallow 是标配
ts
import { shallow } from 'zustand/shallow';
const { user, token } = useAuthStore(
(s) => ({ user: s.user, token: s.token }),
shallow
);
适用于:
- Header
- TabBar
- List Item
3. 高频 UI 状态单独 Store
ts
type UIState = {
loading: boolean;
show: () => void;
hide: () => void;
};
避免与业务状态混用,减少渲染链路。
六、异步数据 + 列表场景实践
列表 Store 示例
ts
type ListState = {
list: any[];
page: number;
loading: boolean;
refreshing: boolean;
fetch: (refresh?: boolean) => Promise<void>;
};
export const useListStore = create<ListState>((set, get) => ({
list: [],
page: 1,
loading: false,
refreshing: false,
fetch: async (refresh = false) => {
const page = refresh ? 1 : get().page;
set(refresh ? { refreshing: true } : { loading: true });
const data = await fetchListApi(page);
set((state) => ({
list: refresh ? data : [...state.list, ...data],
page: page + 1,
loading: false,
refreshing: false,
}));
},
}));
FlatList 使用
tsx
const list = useListStore((s) => s.list);
const fetch = useListStore((s) => s.fetch);
const refreshing = useListStore((s) => s.refreshing);
<FlatList
data={list}
onRefresh={() => fetch(true)}
refreshing={refreshing}
onEndReached={() => fetch(false)}
/>
逻辑集中、UI 干净,非常适合 RN。
七、持久化:Zustand + AsyncStorage
ts
import { persist } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
export const useAuthStore = create(
persist(
(set) => ({
token: '',
setToken: (t: string) => set({ token: t }),
clear: () => set({ token: '' }),
}),
{
name: 'auth-store',
storage: {
getItem: AsyncStorage.getItem,
setItem: AsyncStorage.setItem,
removeItem: AsyncStorage.removeItem,
},
}
)
);
注意点
- 冷启动恢复是 异步的
- 首屏渲染要考虑 token 未恢复完成的情况
八、Zustand vs Redux Toolkit(RN 实战结论)
| 维度 | Zustand | RTK |
|---|---|---|
| 代码量 | 极少 | 多 |
| 心智负担 | 低 | 中 |
| RN Module | 非常适合 | 一般 |
| 超大型项目 | 一般 | 更稳妥 |
实际建议
- RN 业务模块 / 子工程:Zustand
- 跨端统一状态 / 超复杂逻辑:RTK
- 两者完全可以共存
九、真实项目中的使用边界
Zustand 非万能:
不适合的场景:
- 复杂状态流转(有限状态机)
- 强约束的数据一致性
- 超大团队协作的核心状态
非常适合的场景:
- 页面级 / 模块级状态
- UI 状态管理
- Hybrid RN 工程
十、总结
Zustand 在 React Native 中的价值不在于"替代 Redux",而在于:
- 降低状态管理门槛
- 提升开发效率
- 保持工程结构清晰
- 对 Hybrid 架构极度友好
用对地方,它就是 RN 项目的效率放大器。
