React Native状态管理深度剖析
React Native 状态管理深度剖析
在构建 React Native 应用时,状态管理 是核心话题之一。无论是简单的本地组件状态,还是跨多个页面、甚至全局共享的状态,都直接影响应用的可维护性、性能和可扩展性。本文将从状态管理的基本概念入手,详细剖析 React Native 中常见的状态管理方案,包括:
- 本地组件状态:
useState
、useReducer
- Context API:适用于轻量级全局状态
- Redux:最常用的集中式状态管理
- MobX:基于可观察数据的响应式状态管理
- Recoil:Facebook 出品的现代状态管理方案
- Zustand & React Query 等轻量方案
每一部分都包含原理解析、代码示例、图解,帮助你迅速理解并在项目中灵活运用。
目录
- 状态管理概述
- 本地组件状态(useState 与 useReducer)
- Context API:轻量级全局共享
- 原理解析
- 安装与基本配置
- Action、Reducer 与 Store
- React Redux 连接组件
- 中间件:Redux Thunk / Saga
- 代码示例
- 状态流图解
- 原理解析
- 安装与配置
- 可观察(observable)与动作(action)
- 代码示例
- 响应式更新图解
- 原理解析
- 安装与配置
- Atom 与 Selector
- 代码示例
- 数据流图解
- 原理解析
- 安装与配置
- 代码示例
- 原理解析
- 安装与配置
- 代码示例
- 如何选择合适的方案?
- 总结
1. 状态管理概述
在 React(包括 React Native)应用中,“状态”指的是影响界面呈现的一切动态数据,例如用户输入、网络请求结果、导航路由、全局配置、鉴权信息等。状态管理 则是指如何存储、读取、更新以及订阅这些动态数据。
常见需求包括:
- 局部状态:只在一个组件内部使用,例如表单输入字段的内容、动画播放状态等
- 全局状态:在多个组件之间共享,例如用户登入状态、主题色、购物车数据等
- 异步数据:从后端获取的网络数据,需要做加载、缓存、错误处理
- 衍生状态:基于现有状态计算而来,例如过滤后的列表、分页后的数据
不同场景下,我们需要不同粒度的状态管理方案:
- 组件内部状态:用
useState
、useReducer
足够 - 跨组件共享但轻量:Context API 配合
useReducer
即可 - 复杂业务、多人协作、需要中间件:推荐 Redux
- 响应式、面向对象风格:MobX
- 现代 Hooks 与 DSL:Recoil、Zustand
- 数据获取 & 缓存管理:React Query
下面分别讲解每种方案的原理、优势与劣势,并附上代码示例及图解。
2. 本地组件状态(useState 与 useReducer)
2.1 useState
useState
是函数组件管理简单局部状态的最常用 Hook。示例:
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<View style={{ alignItems: 'center', marginTop: 50 }}>
<Text style={{ fontSize: 24 }}>当前计数:{count}</Text>
<Button title="增加" onPress={() => setCount(count + 1)} />
<Button title="重置" onPress={() => setCount(0)} />
</View>
);
}
- 原理:
useState
在组件首次渲染时会创建一个内部存储槽,保存初始状态。后续每次调用setCount
,React 会将新的count
存入该槽并触发组件重渲染。 - 适用场景:简单标量、布尔、字符串、数组、对象等本地状态,无需复杂逻辑时优先考虑。
2.2 useReducer
当状态逻辑复杂,涉及多个子值或者下一个状态依赖前一个状态时,推荐用 useReducer
。示例:
import React, { useReducer } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
// 定义 reducer 函数
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error(`未知 action: ${action.type}`);
}
}
export default function CounterWithReducer() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<View style={styles.container}>
<Text style={styles.text}>当前计数:{state.count}</Text>
<View style={styles.buttons}>
<Button title="增加" onPress={() => dispatch({ type: 'increment' })} />
<Button title="减少" onPress={() => dispatch({ type: 'decrement' })} />
<Button title="重置" onPress={() => dispatch({ type: 'reset' })} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { alignItems: 'center', marginTop: 50 },
text: { fontSize: 24, marginBottom: 20 },
buttons: { flexDirection: 'row', justifyContent: 'space-between', width: 250 },
});
- 原理:
useReducer
接受一个 reducer 函数和初始状态,返回当前状态和dispatch
函数。dispatch
接受一个 action,对应 reducer 返回新状态。 - 适用场景:状态逻辑复杂,或多个状态值有依赖时。例如:表单状态管理(验证、提交等)、购物车添加/删除逻辑、游戏状态机等。
3. Context API 轻量级全局共享
当需要跨若干个深层组件共享状态,但项目不想引入 Redux 时,可使用 Context API。Context 通过组件树传递值,避免逐层传递 props。
3.1 创建 Context
// src/contexts/AuthContext.js
import React, { createContext, useState } from 'react';
export const AuthContext = createContext({
user: null,
login: () => {},
logout: () => {},
});
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (username) => setUser({ username });
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
AuthContext.Provider
将user
、login
、logout
暴露给整个子树。
3.2 在组件中使用
// src/screens/ProfileScreen.js
import React, { useContext } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { AuthContext } from '../contexts/AuthContext';
export default function ProfileScreen() {
const { user, logout } = useContext(AuthContext);
return (
<View style={styles.container}>
{user ? (
<>
<Text style={styles.text}>欢迎,{user.username}!</Text>
<Button title="退出登录" onPress={logout} />
</>
) : (
<Text style={styles.text}>请先登录</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
text: { fontSize: 18 },
});
在根组件包裹提供者:
// App.js
import React from 'react';
import { AuthProvider } from './src/contexts/AuthContext';
import ProfileScreen from './src/screens/ProfileScreen';
export default function App() {
return (
<AuthProvider>
<ProfileScreen />
</AuthProvider>
);
}
图解:Context 数据流
┌────────────────────────────┐
│ AuthProvider │
│ user, login, logout 值存在 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ ProfileScreen & Siblings │
│ useContext(AuthContext) │
└────────────────────────────┘
- Context 可在任意深度的子组件中直接获取,无需逐层传递 props。
- 注意:Context 过度使用会导致组件重渲染范围过大,性能受影响。仅在真正需要跨多层共享时使用。
4. Redux 集中式状态管理
Redux 是最常见、最成熟的集中式状态管理方案。它将整个应用的状态存储在一个统一的 Store 中,通过Action → Reducer → Store 的模式更新数据,并通过 订阅(subscribe / react-redux) 驱动 UI 更新。
4.1 原理解析
- Store:一个包含全局状态树的对象,只能通过 dispatch Action 来更新。
- Action:一个普通对象,描述“发生了什么”,至少包含
type
字段,可携带payload
。 - Reducer:一个纯函数,接收当前 state 和 action,返回新的 state。
- Dispatch:向 Store 发送 Action,触发 Reducer 更新状态。
- 订阅(subscribe/mapStateToProps):React-Redux 将 Store 的 state 通过
mapStateToProps
或useSelector
绑定到组件,当 state 更新时,组件自动重新渲染。
Redux 数据流图解:
┌────────────┐
│ Component │
│ dispatch() │
└─────┬──────┘
│ Action
▼
┌────────────┐
│ Store │
│ Reducer │<── current State + Action → new State
│ │
└─────┬──────┘
│ 订阅通知
▼
┌────────────┐
│ Components │ 重新读取新 State 渲染 UI
└────────────┘
4.2 安装与基本配置
yarn add redux react-redux
# 如果需要异步处理
yarn add redux-thunk
4.2.1 创建 Store
// src/store/index.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
// 1. 导入 Reducer
import authReducer from './reducers/auth';
import dataReducer from './reducers/data';
// 2. 合并 Reducer
const rootReducer = combineReducers({
auth: authReducer,
data: dataReducer,
});
// 3. 创建 Store,应用中间件
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;
4.2.2 连接根组件
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './src/store';
import MainNavigator from './src/navigation/MainNavigator';
export default function App() {
return (
<Provider store={store}>
<MainNavigator />
</Provider>
);
}
Provider
使整个组件树能够访问store
。
4.3 Action、Reducer 与 Store
4.3.1 定义 Action Types 与 Action Creators
// src/store/actions/authActions.js
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGOUT = 'LOGOUT';
export const login = (username) => (dispatch) => {
// 异步示例:模拟登录接口
setTimeout(() => {
dispatch({ type: LOGIN_SUCCESS, payload: { username } });
}, 1000);
};
export const logout = () => ({ type: LOGOUT });
4.3.2 定义 Reducer
// src/store/reducers/auth.js
import { LOGIN_SUCCESS, LOGOUT } from '../actions/authActions';
const initialState = {
user: null,
loading: false,
};
export default function authReducer(state = initialState, action) {
switch (action.type) {
case LOGIN_SUCCESS:
return { ...state, user: action.payload.username };
case LOGOUT:
return { ...state, user: null };
default:
return state;
}
}
4.4 React Redux 连接组件
4.4.1 useSelector 与 useDispatch
React-Redux 提供了两个 Hook 来绑定 Redux 状态与 dispatch:
useSelector(selector)
:选择需要的 state 片段,并订阅更新。useDispatch()
:返回dispatch
函数,用于分发 Action。
// src/screens/LoginScreen.js
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { login, logout } from '../store/actions/authActions';
export default function LoginScreen() {
const dispatch = useDispatch();
const user = useSelector((state) => state.auth.user);
const [username, setUsername] = useState('');
const handleLogin = () => {
dispatch(login(username));
};
const handleLogout = () => {
dispatch(logout());
};
return (
<View style={styles.container}>
{user ? (
<>
<Text style={styles.text}>欢迎,{user}!</Text>
<Button title="退出登录" onPress={handleLogout} />
</>
) : (
<>
<TextInput
style={styles.input}
placeholder="请输入用户名"
value={username}
onChangeText={setUsername}
/>
<Button title="登录" onPress={handleLogin} />
</>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
text: { fontSize: 24, marginBottom: 20 },
input: {
width: '80%',
height: 44,
borderColor: '#CCC',
borderWidth: 1,
marginBottom: 12,
paddingHorizontal: 8,
},
});
useSelector
自动订阅 Redux Store,当auth.user
改变时,组件会重新渲染。useDispatch
用于派发login
、logout
等异步或同步 Action。
4.5 中间件:Redux Thunk / Redux Saga
如果需要在 Action 中进行异步操作(如网络请求),常用中间件有:
Redux Thunk
- 允许 Action Creator 返回一个函数
(dispatch, getState) => { ... }
,内部可执行异步逻辑,再根据结果 dispatch 普通 Action。
- 允许 Action Creator 返回一个函数
Redux Saga
- 基于 Generator 函数,监听指定 Action,然后在 Saga 中处理异步调用(call/put/select),对复杂异步逻辑有更好的组织能力。
本文仅展示 Thunk 示例,如果需进一步了解 Saga,可另行查阅。
4.6 完整代码示例:Todo 应用
下面以一个简单的 Todo List 应用为例,演示 Redux 流程完整样式。
4.6.1 Action 与 Reducer
// src/store/actions/todoActions.js
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
export const addTodo = (text) => ({
type: ADD_TODO,
payload: { id: Date.now().toString(), text },
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
payload: { id },
});
// src/store/reducers/todoReducer.js
import { ADD_TODO, TOGGLE_TODO } from '../actions/todoActions';
const initialState = { todos: [] };
export default function todoReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return {
todos: [
...state.todos,
{ id: action.payload.id, text: action.payload.text, completed: false },
],
};
case TOGGLE_TODO:
return {
todos: state.todos.map((todo) =>
todo.id === action.payload.id
? { ...todo, completed: !todo.completed }
: todo
),
};
default:
return state;
}
}
4.6.2 Store 配置
// src/store/index.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import todoReducer from './reducers/todoReducer';
import authReducer from './reducers/auth';
const rootReducer = combineReducers({
todos: todoReducer,
auth: authReducer,
});
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;
4.6.3 TodoList 组件
// src/screens/TodoListScreen.js
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
Button,
FlatList,
TouchableOpacity,
StyleSheet,
} from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { addTodo, toggleTodo } from '../store/actions/todoActions';
export default function TodoListScreen() {
const [text, setText] = useState('');
const dispatch = useDispatch();
const todos = useSelector((state) => state.todos.todos);
return (
<View style={styles.container}>
<Text style={styles.title}>Todo List (Redux)</Text>
<View style={styles.inputRow}>
<TextInput
style={styles.input}
placeholder="输入待办项"
value={text}
onChangeText={setText}
/>
<Button
title="添加"
onPress={() => {
if (text.trim()) {
dispatch(addTodo(text));
setText('');
}
}}
/>
</View>
<FlatList
data={todos}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.item}
onPress={() => dispatch(toggleTodo(item.id))}
>
<Text style={item.completed ? styles.doneText : styles.text}>
{item.text}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 16 },
inputRow: { flexDirection: 'row', marginBottom: 12 },
input: {
flex: 1,
borderColor: '#CCC',
borderWidth: 1,
borderRadius: 4,
marginRight: 8,
paddingHorizontal: 8,
height: 44,
},
item: {
paddingVertical: 12,
borderBottomColor: '#EEE',
borderBottomWidth: 1,
},
text: { fontSize: 16 },
doneText: { fontSize: 16, textDecorationLine: 'line-through', color: '#999' },
});
- 点击“添加”会
dispatch(addTodo)
,将新的 TODO 存入 Redux; - 点击某项会
dispatch(toggleTodo)
,切换完成状态。
4.6.4 总体流程图解
┌──────────────┐
│ 用户输入“吃饭” │
└───────┬──────┘
│ dispatch({ type: 'ADD_TODO', payload: { id: '123', text: '吃饭' } })
▼
┌──────────────────┐
│ Redux Middleware │ (thunk,无异步)
└───────┬──────────┘
│
▼
┌──────────────────┐
│ Reducer │
│ state.todos: [] │
│ + action → 新 state: { todos: [{ id: '123', text: '吃饭', completed: false }] }
└───────┬──────────┘
│
▼
┌──────────────────┐
│ React-Redux 订阅 │
│ FlatList 自动更新 │
└──────────────────┘
- Redux 确保所有操作可预测、纯粹,并可通过工具调试。
5. MobX 基于可观察数据的响应式方案
MobX 通过可观察(observable) 和动作(action),实现响应式状态管理。每个可观察状态改变时,使用它的组件会自动重新渲染,类似 Vue 的响应式。
5.1 原理解析
可观察(observable)
- 任何变量(对象、数组、Map、Class 属性等)都可标记为
observable
,当发生变化时,依赖它的组件自动更新。
- 任何变量(对象、数组、Map、Class 属性等)都可标记为
动作(action)
- 修改可观察状态的函数必须标记为
action
,以便 MobX 在事务中跟踪变化。
- 修改可观察状态的函数必须标记为
观察者(observer)
observer
高阶组件(或 Hook)包裹的 React 组件会成为观察者,使用到observable
值时会自动订阅,发生变化时触发 render。
MobX 数据流图解:
┌─────────────────┐ ┌────────────────────────┐
│ observable data │ ──> │ observer Component │
└─────────────────┘ └────────────────────────┘
^ │
│ action 修改 observable │
└───────────────────────────┘
5.2 安装与配置
yarn add mobx mobx-react-lite
5.2.1 创建 Store(Class 或 Hook)
// src/stores/counterStore.js
import { makeAutoObservable } from 'mobx';
class CounterStore {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count += 1;
}
decrement() {
this.count -= 1;
}
reset() {
this.count = 0;
}
}
export const counterStore = new CounterStore();
makeAutoObservable(this)
会将类实例的所有属性标记为可观察,并将所有方法标记为 action。
5.2.2 在组件中使用
// src/screens/CounterWithMobX.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { observer } from 'mobx-react-lite';
import { counterStore } from '../stores/counterStore';
const CounterWithMobX = observer(() => {
return (
<View style={styles.container}>
<Text style={styles.text}>当前计数:{counterStore.count}</Text>
<View style={styles.row}>
<Button title="增加" onPress={() => counterStore.increment()} />
<Button title="减少" onPress={() => counterStore.decrement()} />
<Button title="重置" onPress={() => counterStore.reset()} />
</View>
</View>
);
});
export default CounterWithMobX;
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
text: { fontSize: 24, marginBottom: 20 },
row: { flexDirection: 'row', justifyContent: 'space-around', width: 250 },
});
observer
让组件订阅counterStore.count
,当其变化时重新渲染。counterStore.increment()
是 action,会自动批量更新状态并触发订阅。
5.3 响应式更新图解
┌───────────────────────┐
│ counterStore.count │──┐
└───────────────────────┘ │
▲ │
│ observer 订阅 │
│ ▼
┌───────────────────────┐ ┌────────────────────────┐
│ CounterWithMobX │ │ React 原生渲染引擎 │
│ <Text>{count}</Text> │ │ 更新 UI │
└───────────────────────┘ └────────────────────────┘
▲
│ action
│ counterStore.increment()
│
┌───────────────────────┐
│ makeAutoObservable│
│ 标记为可观察/动作 │
└───────────────────────┘
- MobX 的响应式机制基于 Getter/Setter、Proxy 等技术,一旦
count
变化,CounterWithMobX
会自动重新渲染。
6. Recoil(Facebook 出品的现代状态管理)
Recoil 是 Facebook 开源的状态管理库,专为 React(包括 React Native)设计,使用Atom 表示可写可读的最小状态单元,Selector 表示基于 Atom 或其他 Selector 的派生状态,具备并发模式和异步查询等特性。
6.1 原理解析
Atom
- 原子状态,可通过
useRecoilState
订阅与更新;多个组件使用同一个 Atom 时,共享同一份状态。
- 原子状态,可通过
Selector
- 派生状态或异步数据查询,将多个 Atom 组合或从后端获取数据;使用
useRecoilValue
读取其值。当依赖的 Atom 更新时,Selector 会重新计算。
- 派生状态或异步数据查询,将多个 Atom 组合或从后端获取数据;使用
RecoilRoot
- 包裹应用,提供 Recoil 状态环境。
Recoil 数据流图解:
┌──────────────────┐ ┌──────────────────┐
│ Atom A │ │ Atom B │
└──────────────────┘ └──────────────────┘
│ │
└─────→ Selector C ←─────┘ (依赖 A、B 或 异步Fetch)
↓
React 组件 使用 C
6.2 安装与配置
yarn add recoil
6.2.1 根组件包裹
// App.js
import React from 'react';
import { RecoilRoot } from 'recoil';
import CounterRecoil from './src/screens/CounterRecoil';
export default function App() {
return (
<RecoilRoot>
<CounterRecoil />
</RecoilRoot>
);
}
6.3 Atom与Selector
6.3.1 定义 Atom
// src/state/counterAtom.js
import { atom } from 'recoil';
export const counterAtom = atom({
key: 'counterAtom', // 唯一 ID
default: 0, // 默认初始值
});
6.3.2 定义 Selector(派生状态示例)
// src/state/counterSelector.js
import { selector } from 'recoil';
import { counterAtom } from './counterAtom';
export const doubleCounterSelector = selector({
key: 'doubleCounterSelector',
get: ({ get }) => {
const count = get(counterAtom);
return count * 2;
},
});
6.4 代码示例
// src/screens/CounterRecoil.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { useRecoilState, useRecoilValue } from 'recoil';
import { counterAtom } from '../state/counterAtom';
import { doubleCounterSelector } from '../state/counterSelector';
export default function CounterRecoil() {
const [count, setCount] = useRecoilState(counterAtom);
const doubleCount = useRecoilValue(doubleCounterSelector);
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<Text style={styles.text}>Double Count: {doubleCount}</Text>
<View style={styles.row}>
<Button title="增加" onPress={() => setCount(count + 1)} />
<Button title="减少" onPress={() => setCount(count - 1)} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
text: { fontSize: 20, marginVertical: 8 },
row: { flexDirection: 'row', width: 200, justifyContent: 'space-between' },
});
useRecoilState(counterAtom)
返回[count, setCount]
;useRecoilValue(doubleCounterSelector)
自动订阅counterAtom
,当其变化时重新计算doubleCount
并更新 UI。
7. Zustand 更轻量的状态管理
Zustand 是一个更轻量、API 简洁的状态管理库,基于 Hooks,无需样板代码。
7.1 原理解析
create
- 通过
create
创建一个全局 store,内部使用原生可观察(subscribe)机制,无需 Provider。
- 通过
useStore Hook
- 返回状态与 actions,组件调用时自动订阅所使用的状态片段。
Zustand 数据流图解:
┌─────────────────────────────┐
│ create((set, get) => ...) │
│ → 返回 useStore Hook │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ useStore(state => state.x)│ ← 组件订阅 x
└─────────────────────────────┘
▲
│ action 调用 set → 更新状态,触发订阅
│
┌─────────────────────────────┐
│ state = { ... } │
└─────────────────────────────┘
7.2 安装与配置
yarn add zustand
7.2.1 创建 Store
// src/store/zustandStore.js
import create from 'zustand';
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 }),
}));
create
接受一个函数,函数参数(set, get)
,返回一个包含状态和操作的对象。
7.2.2 在组件中使用
// src/screens/CounterZustand.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { useCounterStore } from '../store/zustandStore';
export default function CounterZustand() {
const { count, increment, decrement, reset } = useCounterStore((state) => ({
count: state.count,
increment: state.increment,
decrement: state.decrement,
reset: state.reset,
}));
return (
<View style={styles.container}>
<Text style={styles.text}>当前计数:{count}</Text>
<View style={styles.row}>
<Button title="增加" onPress={increment} />
<Button title="减少" onPress={decrement} />
<Button title="重置" onPress={reset} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
text: { fontSize: 24, marginBottom: 20 },
row: { flexDirection: 'row', justifyContent: 'space-around', width: 250 },
});
useCounterStore(state => ({ ... }))
只订阅所需的属性,性能友好。
8. React Query:数据获取与缓存管理
虽然不专门用于“UI 状态”管理,但 React Query 在服务端状态(数据获取、缓存、刷新)方面表现卓越。将其与上述状态管理方案结合,可形成全方位的状态解决方案。
8.1 原理解析
查询缓存(Query Cache)
- 对网络请求进行缓存、去重、过期等管理。
自动重新触发
- 当组件挂载时自动拉取数据;当数据失效或聚焦时重新拉取。
Mutation 管理
- 提供对数据变更(POST、PUT、DELETE)的抽象,并支持乐观更新。
React Query 数据流图解:
┌────────────────────────────┐
│ useQuery('todos', fetch) │
└───────┬────────────────────┘
│
▼
┌────────────────────────────┐
│ Query Cache (key=todos) │
│ • 若缓存存在且未过期 → 返回 │
│ • 否 → 发起 fetch 请求 │
└───────┬────────────────────┘
│ fetch 成功
▼
┌────────────────────────────┐
│ 更新缓存并触发订阅组件渲染 │
└────────────────────────────┘
8.2 安装与使用
yarn add @tanstack/react-query
8.2.1 在根组件配置 QueryClient
// App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import TodoListScreen from './src/screens/TodoListScreen';
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<TodoListScreen />
</QueryClientProvider>
);
}
8.2.2 示例:获取 TODO 列表
// src/screens/TodoListScreen.js
import React from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator } from 'react-native';
import { useQuery } from '@tanstack/react-query';
// 模拟 API
async function fetchTodos() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos?_limit=10');
return response.json();
}
export default function TodoListScreen() {
const { data, error, isLoading, isError } = useQuery(['todos'], fetchTodos);
if (isLoading) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
if (isError) {
return (
<View style={styles.center}>
<Text>Error: {error.message}</Text>
</View>
);
}
return (
<FlatList
data={data}
keyExtractor={(item) => `${item.id}`}
renderItem={({ item }) => (
<View style={styles.item}>
<Text>{item.title}</Text>
</View>
)}
/>
);
}
const styles = StyleSheet.create({
center: { flex: 1, alignItems: 'center', justifyContent: 'center' },
item: { padding: 12, borderBottomWidth: 1, borderBottomColor: '#EEE' },
});
useQuery(['todos'], fetchTodos)
首次挂载时调用fetchTodos
,并缓存结果;- 当组件再次挂载或参数变化,会根据缓存策略决定是否重新请求。
9. 如何选择合适的方案?
不同项目规模、业务复杂度和团队偏好决定最佳方案。下面给出几点参考建议:
项目较小、团队熟练 Hooks
- 只需
useState
+ Context 即可;若仅需一个简单的全局状态(鉴权、主题切换),Context + useReducer 性能与维护成本最小。
- 只需
中大型项目、多人协作、需要可视化调试
- Redux 是最成熟的解决方案,拥有丰富的生态(Redux DevTools、Middlewares、Redux Toolkit、Redux Persist 等),适合复杂业务。
需要响应式开发、面向对象风格
- MobX 使得状态与 UI 响应式耦合较好,上手简单,适合那些倾向于“类 + 装饰器”语法的团队。但要注意飙升的可观察数据量会增加调试难度。
追求现代化 Hook 体验
- Recoil 提供了 Atom/Selector 的 DSL,原生支持并发模式与异步数据流,适合对 React 性能有更高要求的团队。
轻量 & 无 Provider
- Zustand 极简 API,适合要快速上手,无需写大量模板代码的场景。
数据获取 & 缓存管理
- 对于服务端数据,React Query(或 SWR)是最佳选择,与上述任何状态管理方案结合都很自然。
方案 | 典型规模 | 学习曲线 | 优点 | 缺点 |
---|---|---|---|---|
useState/useReducer + Context | 小型 | 低 | 无额外依赖、易上手、轻量 | 随项目增多,Context 参数膨胀、性能难优化 |
Redux | 中大型 | 中等 | 可视化调试、丰富生态、社区成熟 | Boilerplate 较多、样板代码多 |
MobX | 中大型 | 中等 | 响应式自动更新、面向对象风格 | 可观察链路复杂时性能调优较难 |
Recoil | 中型-大型 | 中等 | 原生 Hook、并发安全、异步支持 | 生态相对年轻、社区资源偏少 |
Zustand | 小型-中型 | 低 | API 极简、无 Provider、轻量 | 无丰富插件生态、纯 JS 管理需谨慎 |
React Query | 所有规模 | 低 | 专注数据获取与缓存、自动重试 | 仅服务端数据,不适合 UI 状态管理 |
10. 总结
本文从基础到进阶,全面剖析了 React Native 中常见的状态管理方案:
- 本地组件状态:
useState
与useReducer
- Context API:轻量级全局状态共享
- Redux:集中式、可视化、可扩展、适合复杂场景
- MobX:基于可观察数据的响应式方案
- Recoil:Facebook 出品的现代 Hook 状态管理
- Zustand:更轻量无 Provider 的方案
- React Query:专注服务端数据获取与缓存管理
每种方案都有其适用场景与优缺点,关键在于根据项目规模、团队技术栈与业务需求 做出合理选择。常见做法是:
- 让小型项目直接用
useState
+ Context; - 中大型项目用 Redux 管理全局状态,React Query 管理网络请求;
- 希望更简洁或响应式开发的团队,可以尝试 MobX、Recoil 或 Zustand。
不论你选择哪种方案,都要牢记核心原则:状态驱动视图。良好的状态管理方案能让你的 React Native 应用更易维护、更具可读性、更高性能。希望这篇深度剖析能帮助你更好地理解并运用各种状态管理技术,为项目保驾护航。愿你在 React Native 的开发道路上越走越顺,打造出高质量的移动应用!
评论已关闭