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