React Native状态管理基础 #

概述 #

状态管理是 React Native 应用开发的核心。本章节介绍从简单到复杂的状态管理方案,帮助你选择合适的方案。

useState #

最基础的状态管理方式,适用于组件内部状态。

基本用法 #

tsx
import React, {useState} from 'react';
import {View, Text, Button, TextInput, StyleSheet} from 'react-native';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      <Text style={styles.count}>{count}</Text>
      <Button title="Increase" onPress={() => setCount(count + 1)} />
      <Button title="Decrease" onPress={() => setCount(count - 1)} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  count: {
    fontSize: 48,
    fontWeight: 'bold',
  },
});

export default Counter;

对象状态 #

tsx
interface User {
  name: string;
  email: string;
  age: number;
}

const UserForm = () => {
  const [user, setUser] = useState<User>({
    name: '',
    email: '',
    age: 0,
  });

  const updateName = (name: string) => {
    setUser(prev => ({...prev, name}));
  };

  const updateEmail = (email: string) => {
    setUser(prev => ({...prev, email}));
  };

  return (
    <View style={styles.container}>
      <TextInput
        value={user.name}
        onChangeText={updateName}
        placeholder="Name"
        style={styles.input}
      />
      <TextInput
        value={user.email}
        onChangeText={updateEmail}
        placeholder="Email"
        style={styles.input}
      />
      <Text>{JSON.stringify(user, null, 2)}</Text>
    </View>
  );
};

数组状态 #

tsx
const TodoList = () => {
  const [todos, setTodos] = useState<string[]>([]);
  const [input, setInput] = useState('');

  const addTodo = () => {
    if (input.trim()) {
      setTodos(prev => [...prev, input]);
      setInput('');
    }
  };

  const removeTodo = (index: number) => {
    setTodos(prev => prev.filter((_, i) => i !== index));
  };

  return (
    <View style={styles.container}>
      <View style={styles.inputRow}>
        <TextInput
          value={input}
          onChangeText={setInput}
          placeholder="Add todo"
          style={styles.input}
        />
        <Button title="Add" onPress={addTodo} />
      </View>
      <FlatList
        data={todos}
        keyExtractor={(item, index) => index.toString()}
        renderItem={({item, index}) => (
          <View style={styles.todoItem}>
            <Text>{item}</Text>
            <Button title="Delete" onPress={() => removeTodo(index)} />
          </View>
        )}
      />
    </View>
  );
};

useReducer #

适用于复杂状态逻辑。

基本用法 #

tsx
import React, {useReducer} from 'react';
import {View, Text, Button, StyleSheet} from 'react-native';

type State = {
  count: number;
};

type Action =
  | {type: 'increment'}
  | {type: 'decrement'}
  | {type: 'reset'}
  | {type: 'set'; payload: number};

const initialState: State = {count: 0};

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return initialState;
    case 'set':
      return {count: action.payload};
    default:
      return state;
  }
};

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

  return (
    <View style={styles.container}>
      <Text style={styles.count}>{state.count}</Text>
      <View style={styles.buttons}>
        <Button title="-" onPress={() => dispatch({type: 'decrement'})} />
        <Button title="Reset" onPress={() => dispatch({type: 'reset'})} />
        <Button title="+" onPress={() => dispatch({type: 'increment'})} />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  count: {
    fontSize: 48,
    fontWeight: 'bold',
  },
  buttons: {
    flexDirection: 'row',
    gap: 10,
  },
});

export default Counter;

复杂状态示例 #

tsx
interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

type TodoAction =
  | {type: 'ADD'; payload: string}
  | {type: 'TOGGLE'; payload: string}
  | {type: 'DELETE'; payload: string}
  | {type: 'CLEAR_COMPLETED'};

const todoReducer = (state: Todo[], action: TodoAction): Todo[] => {
  switch (action.type) {
    case 'ADD':
      return [
        ...state,
        {id: Date.now().toString(), text: action.payload, completed: false},
      ];
    case 'TOGGLE':
      return state.map(todo =>
        todo.id === action.payload
          ? {...todo, completed: !todo.completed}
          : todo,
      );
    case 'DELETE':
      return state.filter(todo => todo.id !== action.payload);
    case 'CLEAR_COMPLETED':
      return state.filter(todo => !todo.completed);
    default:
      return state;
  }
};

const TodoApp = () => {
  const [todos, dispatch] = useReducer(todoReducer, []);
  const [input, setInput] = useState('');

  const addTodo = () => {
    if (input.trim()) {
      dispatch({type: 'ADD', payload: input});
      setInput('');
    }
  };

  return (
    <View style={styles.container}>
      <View style={styles.inputRow}>
        <TextInput
          value={input}
          onChangeText={setInput}
          placeholder="Add todo"
          style={styles.input}
        />
        <Button title="Add" onPress={addTodo} />
      </View>
      <FlatList
        data={todos}
        keyExtractor={item => item.id}
        renderItem={({item}) => (
          <TouchableOpacity
            style={styles.todoItem}
            onPress={() => dispatch({type: 'TOGGLE', payload: item.id})}>
            <Text
              style={[
                styles.todoText,
                item.completed && styles.completedText,
              ]}>
              {item.text}
            </Text>
            <Button
              title="Delete"
              onPress={() => dispatch({type: 'DELETE', payload: item.id})}
            />
          </TouchableOpacity>
        )}
      />
    </View>
  );
};

Context API #

用于跨组件共享状态。

创建 Context #

tsx
import React, {createContext, useContext, useState, ReactNode} from 'react';

interface User {
  id: string;
  name: string;
  email: string;
}

interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
  isLoading: boolean;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

interface AuthProviderProps {
  children: ReactNode;
}

export const AuthProvider: React.FC<AuthProviderProps> = ({children}) => {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  const login = async (email: string, password: string) => {
    setIsLoading(true);
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({email, password}),
      });
      const data = await response.json();
      setUser(data.user);
    } finally {
      setIsLoading(false);
    }
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{user, login, logout, isLoading}}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

使用 Context #

tsx
import React from 'react';
import {View, Text, Button, TextInput, StyleSheet} from 'react-native';
import {AuthProvider, useAuth} from './AuthContext';

const LoginScreen = () => {
  const {login, isLoading} = useAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = () => {
    login(email, password);
  };

  return (
    <View style={styles.container}>
      <TextInput
        value={email}
        onChangeText={setEmail}
        placeholder="Email"
        style={styles.input}
      />
      <TextInput
        value={password}
        onChangeText={setPassword}
        placeholder="Password"
        secureTextEntry
        style={styles.input}
      />
      <Button
        title={isLoading ? 'Loading...' : 'Login'}
        onPress={handleLogin}
        disabled={isLoading}
      />
    </View>
  );
};

const ProfileScreen = () => {
  const {user, logout} = useAuth();

  return (
    <View style={styles.container}>
      <Text>Welcome, {user?.name}</Text>
      <Text>Email: {user?.email}</Text>
      <Button title="Logout" onPress={logout} />
    </View>
  );
};

const App = () => {
  return (
    <AuthProvider>
      <NavigationContainer>
        {}
      </NavigationContainer>
    </AuthProvider>
  );
};

Context + useReducer #

结合使用实现全局状态管理。

tsx
import React, {createContext, useContext, useReducer, ReactNode} from 'react';

interface AppState {
  user: User | null;
  theme: 'light' | 'dark';
  notifications: Notification[];
}

type AppAction =
  | {type: 'SET_USER'; payload: User | null}
  | {type: 'SET_THEME'; payload: 'light' | 'dark'}
  | {type: 'ADD_NOTIFICATION'; payload: Notification}
  | {type: 'CLEAR_NOTIFICATIONS'};

const initialState: AppState = {
  user: null,
  theme: 'light',
  notifications: [],
};

const appReducer = (state: AppState, action: AppAction): AppState => {
  switch (action.type) {
    case 'SET_USER':
      return {...state, user: action.payload};
    case 'SET_THEME':
      return {...state, theme: action.payload};
    case 'ADD_NOTIFICATION':
      return {...state, notifications: [...state.notifications, action.payload]};
    case 'CLEAR_NOTIFICATIONS':
      return {...state, notifications: []};
    default:
      return state;
  }
};

const AppContext = createContext<{
  state: AppState;
  dispatch: React.Dispatch<AppAction>;
} | undefined>(undefined);

export const AppProvider: React.FC<{children: ReactNode}> = ({children}) => {
  const [state, dispatch] = useReducer(appReducer, initialState);

  return (
    <AppContext.Provider value={{state, dispatch}}>
      {children}
    </AppContext.Provider>
  );
};

export const useAppState = () => {
  const context = useContext(AppContext);
  if (!context) {
    throw new Error('useAppState must be used within AppProvider');
  }
  return context;
};

Redux Toolkit #

适用于大型应用的复杂状态管理。

安装 #

bash
npm install @reduxjs/toolkit react-redux

创建 Slice #

tsx
import {createSlice, PayloadAction} from '@reduxjs/toolkit';

interface CounterState {
  value: number;
}

const initialState: CounterState = {
  value: 0,
};

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: state => {
      state.value += 1;
    },
    decrement: state => {
      state.value -= 1;
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload;
    },
    reset: state => {
      state.value = 0;
    },
  },
});

export const {increment, decrement, incrementByAmount, reset} = counterSlice.actions;
export default counterSlice.reducer;

配置 Store #

tsx
import {configureStore} from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import userReducer from './userSlice';

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

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

使用 Redux #

tsx
import React from 'react';
import {View, Text, Button, StyleSheet} from 'react-native';
import {Provider, useSelector, useDispatch} from 'react-redux';
import {store, RootState} from './store';
import {increment, decrement, reset} from './counterSlice';

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

  return (
    <View style={styles.container}>
      <Text style={styles.count}>{count}</Text>
      <View style={styles.buttons}>
        <Button title="-" onPress={() => dispatch(decrement())} />
        <Button title="Reset" onPress={() => dispatch(reset())} />
        <Button title="+" onPress={() => dispatch(increment())} />
      </View>
    </View>
  );
};

const App = () => {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  count: {
    fontSize: 48,
    fontWeight: 'bold',
  },
  buttons: {
    flexDirection: 'row',
    gap: 10,
  },
});

export default App;

异步 Thunk #

tsx
import {createAsyncThunk, createSlice} from '@reduxjs/toolkit';

interface UserState {
  data: User | null;
  loading: boolean;
  error: string | null;
}

const initialState: UserState = {
  data: null,
  loading: false,
  error: null,
};

export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (userId: string, {rejectWithValue}) => {
    try {
      const response = await fetch(`/api/users/${userId}`);
      if (!response.ok) {
        throw new Error('Failed to fetch user');
      }
      return await response.json();
    } catch (error) {
      return rejectWithValue(error.message);
    }
  },
);

const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    clearUser: state => {
      state.data = null;
      state.error = null;
    },
  },
  extraReducers: builder => {
    builder
      .addCase(fetchUser.pending, state => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false;
        state.data = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload as string;
      });
  },
});

状态管理方案对比 #

方案 适用场景 复杂度
useState 组件内部简单状态
useReducer 组件内部复杂状态
Context 跨组件共享简单状态
Context + useReducer 中等规模应用全局状态
Redux Toolkit 大型应用复杂状态管理

最佳实践 #

状态最小化 #

只存储必要的状态,能计算得出的不存储:

tsx
const [items, setItems] = useState<Item[]>([]);

const totalPrice = items.reduce((sum, item) => sum + item.price, 0);
const itemCount = items.length;

状态就近原则 #

状态应该放在最近的共同父组件中:

tsx
const Parent = () => {
  const [selectedId, setSelectedId] = useState<string | null>(null);

  return (
    <View>
      <List items={items} selectedId={selectedId} onSelect={setSelectedId} />
      <Detail item={items.find(i => i.id === selectedId)} />
    </View>
  );
};

避免过度使用 Context #

Context 更新会导致所有消费者重新渲染,谨慎使用。

总结 #

React Native 提供了多种状态管理方案:

  • useState:简单状态
  • useReducer:复杂状态逻辑
  • Context:跨组件共享
  • Redux Toolkit:大型应用

根据应用规模和复杂度选择合适的方案。

继续学习 AsyncStorage,了解本地数据持久化。

最后更新:2026-03-28