状态管理的演变
React 生态的状态管理方案经历了激烈的演变:早期 Redux 一统天下,但样板代码繁多;MobX 以响应式编程简化了写法;Recoil 尝试原子化状态;Zustand 以极简 API 赢得青睐;而 React Query 的出现让人们意识到:大多数"全局状态"其实根本不是客户端状态,而是服务端状态。
在 React Native 应用中,状态可以分为两大类,它们有根本性的不同,需要用不同工具管理:
客户端状态
- 当前用户信息(已登录)
- UI 状态(Modal 是否打开)
- 主题设置(深色/浅色)
- 购物车商品列表
- 表单输入值
服务端状态
- 帖子列表(来自 API)
- 用户详情(来自 API)
- 评论数据(来自 API)
- 搜索结果
- 通知列表
核心名词解释
状态流向图
Zustand:轻量全局状态
Zustand 是目前 React Native 社区最受欢迎的全局状态库之一。它的核心优势是:无需 Provider 包裹,任何组件都可以直接访问 Store;选择器机制确保只有订阅的状态变化时才重渲染。
// store/authStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
interface User {
id: string;
name: string;
avatar: string;
}
interface AuthState {
user: User | null;
token: string | null;
isLoggedIn: boolean;
login: (user: User, token: string) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
// persist 中间件:自动持久化到 AsyncStorage
persist(
(set) => ({
user: null,
token: null,
isLoggedIn: false,
login: (user, token) => set({ user, token, isLoggedIn: true }),
logout: () => set({ user: null, token: null, isLoggedIn: false }),
}),
{
name: 'auth-storage',
storage: createJSONStorage(() => AsyncStorage),
}
)
);
// 使用:只订阅 user,user 变化才重渲染,token 变化不触发
function ProfileHeader() {
const user = useAuthStore(state => state.user); // 精确订阅
const logout = useAuthStore(state => state.logout);
if (!user) return null;
return (
<View>
<Text>{user.name}</Text>
<Pressable onPress={logout}><Text>退出</Text></Pressable>
</View>
);
}
React Query:服务端状态管理
// App.tsx — 配置 QueryClient
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5分钟内不重新请求
retry: 2, // 失败重试2次
refetchOnWindowFocus: false, // App 切回前台时不自动刷新
},
},
});
export default function App() {
return (
<QueryClientProvider client={queryClient}>
{/* 导航和其他内容 */}
</QueryClientProvider>
);
}
// hooks/usePosts.ts — 封装数据请求
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '../lib/api';
export function usePosts() {
return useQuery({
queryKey: ['posts'], // 缓存键,唯一标识这份数据
queryFn: () => api.getPosts(),
});
}
export function useLikePost() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (postId: string) => api.likePost(postId),
// 乐观更新:先修改本地缓存,成功后服务器已同步,失败则回滚
onMutate: async (postId) => {
await queryClient.cancelQueries({ queryKey: ['posts'] });
const prev = queryClient.getQueryData(['posts']);
queryClient.setQueryData(['posts'], (old: Post[]) =>
old.map(p => p.id === postId ? { ...p, likes: p.likes + 1 } : p)
);
return { prev }; // 快照,用于回滚
},
onError: (_, __, ctx) => {
if (ctx?.prev) queryClient.setQueryData(['posts'], ctx.prev);
},
});
}
Context:轻量全局状态
Context 不需要引入额外依赖,适合不频繁更新的全局数据(主题、语言、已登录用户)。关键是要避免 Context 粒度过粗——把所有全局状态塞进一个 Context 会导致任何一个状态变化都重渲染所有消费者。
// contexts/ThemeContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';
type Theme = 'dark' | 'light';
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>('dark');
const toggleTheme = () => setTheme(t => t === 'dark' ? 'light' : 'dark');
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 封装为自定义 Hook,提供类型安全和友好错误信息
export function useTheme(): ThemeContextType {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme 必须在 ThemeProvider 内使用');
return ctx;
}
value={{ theme, toggleTheme }}),每次 Provider 重渲染都会创建新对象,导致所有消费者重渲染。解决方案:用 useMemo 包裹 value,或把读(数据)和写(函数)拆分为两个 Context。状态更新频繁时用 Zustand 替代 Context。
状态管理的核心要点:① useState 管理组件内部状态;useReducer 适合 3 个以上相关状态或复杂更新逻辑;Context 适合中低频读取的全局状态(主题、用户信息);② Zustand 是轻量全局状态首选——create() 定义 store,在组件中 useStore(state => state.xxx) 按需订阅,避免不必要的重渲染;③ React Query(TanStack Query)专为服务端状态设计,自动处理加载状态、缓存、重试、后台刷新;useQuery 取数据,useMutation 写数据;④ 乐观更新(Optimistic Update)通过 onMutate 立即更新 UI、onError 回滚来提升用户体验;⑤ 90% 的"全局状态"其实是服务端状态,用 React Query 管理后,Zustand 只需要处理真正的客户端状态(主题、语言等);⑥ Jotai 和 Recoil 基于原子(atom)模型,适合组件树中分散的细粒度状态。