‌React Native状态管理深度剖析‌

React Native 状态管理深度剖析

在构建 React Native 应用时,状态管理 是核心话题之一。无论是简单的本地组件状态,还是跨多个页面、甚至全局共享的状态,都直接影响应用的可维护性、性能和可扩展性。本文将从状态管理的基本概念入手,详细剖析 React Native 中常见的状态管理方案,包括:

  1. 本地组件状态:useStateuseReducer
  2. Context API:适用于轻量级全局状态
  3. Redux:最常用的集中式状态管理
  4. MobX:基于可观察数据的响应式状态管理
  5. Recoil:Facebook 出品的现代状态管理方案
  6. Zustand & React Query 等轻量方案

每一部分都包含原理解析代码示例图解,帮助你迅速理解并在项目中灵活运用。


目录

  1. 状态管理概述
  2. 本地组件状态(useState 与 useReducer)
  3. Context API:轻量级全局共享
  4. Redux:集中式状态管理

    1. 原理解析
    2. 安装与基本配置
    3. Action、Reducer 与 Store
    4. React Redux 连接组件
    5. 中间件:Redux Thunk / Saga
    6. 代码示例
    7. 状态流图解
  5. MobX:基于可观察数据的响应式方案

    1. 原理解析
    2. 安装与配置
    3. 可观察(observable)与动作(action)
    4. 代码示例
    5. 响应式更新图解
  6. Recoil:Facebook 出品的现代状态管理

    1. 原理解析
    2. 安装与配置
    3. Atom 与 Selector
    4. 代码示例
    5. 数据流图解
  7. Zustand:更轻量的状态管理

    1. 原理解析
    2. 安装与配置
    3. 代码示例
  8. React Query:数据获取与缓存管理

    1. 原理解析
    2. 安装与配置
    3. 代码示例
  9. 如何选择合适的方案?
  10. 总结

1. 状态管理概述

在 React(包括 React Native)应用中,“状态”指的是影响界面呈现的一切动态数据,例如用户输入、网络请求结果、导航路由、全局配置、鉴权信息等。状态管理 则是指如何存储、读取、更新以及订阅这些动态数据。

常见需求包括:

  • 局部状态:只在一个组件内部使用,例如表单输入字段的内容、动画播放状态等
  • 全局状态:在多个组件之间共享,例如用户登入状态、主题色、购物车数据等
  • 异步数据:从后端获取的网络数据,需要做加载、缓存、错误处理
  • 衍生状态:基于现有状态计算而来,例如过滤后的列表、分页后的数据

不同场景下,我们需要不同粒度的状态管理方案:

  1. 组件内部状态:用 useStateuseReducer 足够
  2. 跨组件共享但轻量:Context API 配合 useReducer即可
  3. 复杂业务、多人协作、需要中间件:推荐 Redux
  4. 响应式、面向对象风格:MobX
  5. 现代 Hooks 与 DSL:Recoil、Zustand
  6. 数据获取 & 缓存管理: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.Provideruserloginlogout 暴露给整个子树。

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 原理解析

  1. Store:一个包含全局状态树的对象,只能通过 dispatch Action 来更新。
  2. Action:一个普通对象,描述“发生了什么”,至少包含 type 字段,可携带 payload
  3. Reducer:一个纯函数,接收当前 state 和 action,返回新的 state。
  4. Dispatch:向 Store 发送 Action,触发 Reducer 更新状态。
  5. 订阅(subscribe/mapStateToProps):React-Redux 将 Store 的 state 通过 mapStateToPropsuseSelector 绑定到组件,当 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 用于派发 loginlogout 等异步或同步 Action。

4.5 中间件:Redux Thunk / Redux Saga

如果需要在 Action 中进行异步操作(如网络请求),常用中间件有:

  1. Redux Thunk

    • 允许 Action Creator 返回一个函数 (dispatch, getState) => { ... },内部可执行异步逻辑,再根据结果 dispatch 普通 Action。
  2. 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 原理解析

  1. 可观察(observable)

    • 任何变量(对象、数组、Map、Class 属性等)都可标记为 observable,当发生变化时,依赖它的组件自动更新。
  2. 动作(action)

    • 修改可观察状态的函数必须标记为 action,以便 MobX 在事务中跟踪变化。
  3. 观察者(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 原理解析

  1. Atom

    • 原子状态,可通过 useRecoilState 订阅与更新;多个组件使用同一个 Atom 时,共享同一份状态。
  2. Selector

    • 派生状态或异步数据查询,将多个 Atom 组合或从后端获取数据;使用 useRecoilValue 读取其值。当依赖的 Atom 更新时,Selector 会重新计算。
  3. 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 原理解析

  1. create

    • 通过 create 创建一个全局 store,内部使用原生可观察(subscribe)机制,无需 Provider。
  2. 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 原理解析

  1. 查询缓存(Query Cache)

    • 对网络请求进行缓存、去重、过期等管理。
  2. 自动重新触发

    • 当组件挂载时自动拉取数据;当数据失效或聚焦时重新拉取。
  3. 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. 如何选择合适的方案?

不同项目规模、业务复杂度和团队偏好决定最佳方案。下面给出几点参考建议:

  1. 项目较小、团队熟练 Hooks

    • 只需 useState + Context 即可;若仅需一个简单的全局状态(鉴权、主题切换),Context + useReducer 性能与维护成本最小。
  2. 中大型项目、多人协作、需要可视化调试

    • Redux 是最成熟的解决方案,拥有丰富的生态(Redux DevTools、Middlewares、Redux Toolkit、Redux Persist 等),适合复杂业务。
  3. 需要响应式开发、面向对象风格

    • MobX 使得状态与 UI 响应式耦合较好,上手简单,适合那些倾向于“类 + 装饰器”语法的团队。但要注意飙升的可观察数据量会增加调试难度。
  4. 追求现代化 Hook 体验

    • Recoil 提供了 Atom/Selector 的 DSL,原生支持并发模式与异步数据流,适合对 React 性能有更高要求的团队。
  5. 轻量 & 无 Provider

    • Zustand 极简 API,适合要快速上手,无需写大量模板代码的场景。
  6. 数据获取 & 缓存管理

    • 对于服务端数据React Query(或 SWR)是最佳选择,与上述任何状态管理方案结合都很自然。
方案典型规模学习曲线优点缺点
useState/useReducer + Context小型无额外依赖、易上手、轻量随项目增多,Context 参数膨胀、性能难优化
Redux中大型中等可视化调试、丰富生态、社区成熟Boilerplate 较多、样板代码多
MobX中大型中等响应式自动更新、面向对象风格可观察链路复杂时性能调优较难
Recoil中型-大型中等原生 Hook、并发安全、异步支持生态相对年轻、社区资源偏少
Zustand小型-中型API 极简、无 Provider、轻量无丰富插件生态、纯 JS 管理需谨慎
React Query所有规模专注数据获取与缓存、自动重试仅服务端数据,不适合 UI 状态管理

10. 总结

本文从基础到进阶,全面剖析了 React Native 中常见的状态管理方案:

  1. 本地组件状态useStateuseReducer
  2. Context API:轻量级全局状态共享
  3. Redux:集中式、可视化、可扩展、适合复杂场景
  4. MobX:基于可观察数据的响应式方案
  5. Recoil:Facebook 出品的现代 Hook 状态管理
  6. Zustand:更轻量无 Provider 的方案
  7. React Query:专注服务端数据获取与缓存管理

每种方案都有其适用场景与优缺点,关键在于根据项目规模、团队技术栈与业务需求 做出合理选择。常见做法是:

  • 让小型项目直接用 useState + Context;
  • 中大型项目用 Redux 管理全局状态,React Query 管理网络请求;
  • 希望更简洁或响应式开发的团队,可以尝试 MobX、Recoil 或 Zustand。

不论你选择哪种方案,都要牢记核心原则:状态驱动视图。良好的状态管理方案能让你的 React Native 应用更易维护、更具可读性、更高性能。希望这篇深度剖析能帮助你更好地理解并运用各种状态管理技术,为项目保驾护航。愿你在 React Native 的开发道路上越走越顺,打造出高质量的移动应用!

最后修改于:2025年05月29日 11:05

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日