Skip to content

React Native 状态管理

状态管理是 React Native 应用开发中的重要部分,它决定了如何存储和管理应用的数据。本文将介绍 React Native 中常用的状态管理方案。

内置状态管理

1. useState Hook

useState 是 React 内置的 Hook,用于在函数组件中管理状态:

jsx
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

const Counter = () => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('React Native');

  return (
    <View style={styles.container}>
      <Text style={styles.title}>{name}</Text>
      <Text style={styles.count}>Count: {count}</Text>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => setCount(count + 1)}>
        <Text style={styles.buttonText}>Increment</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => setName('RN')}>
        <Text style={styles.buttonText}>Change Name</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  count: {
    fontSize: 18,
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 10,
    borderRadius: 5,
    marginBottom: 10,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
});

export default Counter;

2. useReducer Hook

useReducer 是另一个内置 Hook,适用于管理复杂的状态逻辑:

jsx
import React, { useReducer } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

const initialState = {
  count: 0,
  step: 1,
};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + state.step };
    case 'decrement':
      return { ...state, count: state.count - state.step };
    case 'setStep':
      return { ...state, step: action.payload };
    default:
      return state;
  }
}

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <View style={styles.container}>
      <Text style={styles.count}>Count: {state.count}</Text>
      <Text style={styles.step}>Step: {state.step}</Text>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => dispatch({ type: 'increment' })}>
        <Text style={styles.buttonText}>Increment</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => dispatch({ type: 'decrement' })}>
        <Text style={styles.buttonText}>Decrement</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => dispatch({ type: 'setStep', payload: 5 })}>
        <Text style={styles.buttonText}>Set Step to 5</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  count: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 10,
  },
  step: {
    fontSize: 18,
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 10,
    borderRadius: 5,
    marginBottom: 10,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
});

export default Counter;

3. Context API

Context API 用于在组件树中传递数据,避免 props drilling:

jsx
import React, { createContext, useContext, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

// 创建 Context
const ThemeContext = createContext();

// 提供者组件
export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// 自定义 Hook
export const useTheme = () => {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
};

// 使用 Context 的组件
const ThemedButton = ({ title, onPress }) => {
  const { theme } = useTheme();
  
  return (
    <TouchableOpacity 
      style={[
        styles.button, 
        theme === 'dark' ? styles.darkButton : styles.lightButton
      ]} 
      onPress={onPress}
    >
      <Text 
        style={[
          styles.buttonText, 
          theme === 'dark' ? styles.darkButtonText : styles.lightButtonText
        ]}
      >
        {title}
      </Text>
    </TouchableOpacity>
  );
};

// 主组件
const App = () => {
  const { theme, toggleTheme } = useTheme();

  return (
    <View style={[
      styles.container, 
      theme === 'dark' ? styles.darkContainer : styles.lightContainer
    ]}>
      <Text style={[
        styles.title, 
        theme === 'dark' ? styles.darkText : styles.lightText
      ]}>
        Current Theme: {theme}
      </Text>
      <ThemedButton title="Toggle Theme" onPress={toggleTheme} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  lightContainer: {
    backgroundColor: '#fff',
  },
  darkContainer: {
    backgroundColor: '#333',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  lightText: {
    color: '#333',
  },
  darkText: {
    color: '#fff',
  },
  button: {
    padding: 10,
    borderRadius: 5,
  },
  lightButton: {
    backgroundColor: '#007AFF',
  },
  darkButton: {
    backgroundColor: '#0056b3',
  },
  buttonText: {
    fontSize: 16,
  },
  lightButtonText: {
    color: '#fff',
  },
  darkButtonText: {
    color: '#fff',
  },
});

export default App;

第三方状态管理库

1. Redux

Redux 是一个流行的状态管理库,适用于管理复杂的应用状态:

安装

bash
# 使用 npm
npm install redux react-redux @reduxjs/toolkit

# 使用 Yarn
yarn add redux react-redux @reduxjs/toolkit

基本用法

jsx
// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

// counterSlice.js
import { createSlice } from '@reduxjs/toolkit';

export 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;

// App.js
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store';
import Counter from './Counter';

export default function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from './counterSlice';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

const Counter = () => {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <View style={styles.container}>
      <Text style={styles.count}>Count: {count}</Text>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => dispatch(increment())}>
        <Text style={styles.buttonText}>Increment</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => dispatch(decrement())}>
        <Text style={styles.buttonText}>Decrement</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => dispatch(incrementByAmount(5))}>
        <Text style={styles.buttonText}>Increment by 5</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  count: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 10,
    borderRadius: 5,
    marginBottom: 10,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
});

export default Counter;

2. MobX

MobX 是另一个流行的状态管理库,使用观察者模式:

安装

bash
# 使用 npm
npm install mobx mobx-react

# 使用 Yarn
yarn add mobx mobx-react

基本用法

jsx
// store.js
import { observable, action, makeAutoObservable } from 'mobx';

class CounterStore {
  @observable count = 0;

  constructor() {
    makeAutoObservable(this);
  }

  @action increment() {
    this.count += 1;
  }

  @action decrement() {
    this.count -= 1;
  }

  @action incrementByAmount(amount) {
    this.count += amount;
  }
}

export default new CounterStore();

// App.js
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { observer } from 'mobx-react';
import counterStore from './store';

const Counter = observer(() => {
  return (
    <View style={styles.container}>
      <Text style={styles.count}>Count: {counterStore.count}</Text>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => counterStore.increment()}>
        <Text style={styles.buttonText}>Increment</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => counterStore.decrement()}>
        <Text style={styles.buttonText}>Decrement</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => counterStore.incrementByAmount(5)}>
        <Text style={styles.buttonText}>Increment by 5</Text>
      </TouchableOpacity>
    </View>
  );
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  count: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 10,
    borderRadius: 5,
    marginBottom: 10,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
});

export default Counter;

3. Recoil

Recoil 是 Facebook 推出的状态管理库,专为 React 设计:

安装

bash
# 使用 npm
npm install recoil

# 使用 Yarn
yarn add recoil

基本用法

jsx
// App.js
import React from 'react';
import { RecoilRoot, atom, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

// 定义 atom
const countState = atom({
  key: 'countState',
  default: 0,
});

// 计数器组件
const Counter = () => {
  const [count, setCount] = useRecoilState(countState);

  return (
    <View style={styles.container}>
      <Text style={styles.count}>Count: {count}</Text>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => setCount(count + 1)}>
        <Text style={styles.buttonText}>Increment</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => setCount(count - 1)}>
        <Text style={styles.buttonText}>Decrement</Text>
      </TouchableOpacity>
      <TouchableOpacity 
        style={styles.button} 
        onPress={() => setCount(count + 5)}>
        <Text style={styles.buttonText}>Increment by 5</Text>
      </TouchableOpacity>
    </View>
  );
};

// 主应用
const App = () => {
  return (
    <RecoilRoot>
      <Counter />
    </RecoilRoot>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  count: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 10,
    borderRadius: 5,
    marginBottom: 10,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
});

export default App;

状态管理最佳实践

  1. 选择合适的状态管理方案:根据应用的复杂度选择合适的状态管理库
  2. 状态分层:将状态分为全局状态、局部状态和UI状态
  3. 状态规范化:使用规范化的状态结构,避免冗余数据
  4. 性能优化:使用 memoization 和选择性渲染提高性能
  5. 测试:为状态管理逻辑编写单元测试
  6. 代码组织:合理组织状态管理相关的代码,提高可维护性

总结

React Native 提供了多种状态管理方案,从内置的 Hooks 到第三方库,开发者可以根据应用的需求选择合适的方案。通过合理的状态管理,可以构建出可维护、可扩展的应用。

基于 VitePress 的本地知识库