Zustand Persist中间件在React Native认证Store中无法正常工作
问题原因分析
- Firebase User对象不可序列化:直接存储Firebase返回的User实例会导致序列化/反序列化丢失数据或生成无效对象,因为User包含不可JSON化的属性(如内部方法、循环引用)。
- 状态覆盖问题:应用重启时,
checkAuthState可能在Zustand persist完成状态恢复前执行,若Firebase此时未返回用户,会将user设为null,覆盖persist存储的数据。 - 异步恢复时机:Zustand persist的状态恢复是异步的,组件可能在恢复完成前就渲染,导致初始状态为
null,误以为登录状态未保持。
解决方案
1. 存储可序列化的用户数据
修改Store,只提取Firebase User中的核心可序列化字段存储,避免直接保存User实例:
import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import { auth } from '../firebase'; import { signInWithEmailAndPassword, onAuthStateChanged } from 'firebase/auth'; import AsyncStorage from '@react-native-async-storage/async-storage'; const useAuthStore = create( persist( (set) => ({ user: null, isAuthLoading: true, checkAuthState: () => { return onAuthStateChanged(auth, (firebaseUser) => { if (firebaseUser) { // 仅存储可序列化的用户核心数据 const userData = { uid: firebaseUser.uid, email: firebaseUser.email, displayName: firebaseUser.displayName, photoURL: firebaseUser.photoURL, }; set({ user: userData, isAuthLoading: false }); } else { set({ user: null, isAuthLoading: false }); } }); }, login: async (email, password) => { try { const userCredential = await signInWithEmailAndPassword(auth, email, password); const firebaseUser = userCredential.user; const userData = { uid: firebaseUser.uid, email: firebaseUser.email, displayName: firebaseUser.displayName, photoURL: firebaseUser.photoURL, }; set({ user: userData }); return userData; } catch (error) { console.log('Error: ', error); throw error; } }, logout: async () => { try { await auth.signOut(); set({ user: null }); } catch (error) { console.log('Error: ', error); throw error; } }, }), { name: "user-state", storage: createJSONStorage(() => AsyncStorage), // 状态恢复完成后更新加载状态 onRehydrateStorage: () => { return (state) => { set({ isAuthLoading: false }); }; }, } ) ); export default useAuthStore;
2. 调整Auth状态监听时机
在主组件中,等待Zustand persist状态恢复完成后,再初始化Firebase的auth状态监听,避免状态覆盖:
import { useEffect } from 'react'; import useAuthStore from './path/to/useAuthStore'; import { auth } from '../firebase'; function App() { const { user, isAuthLoading, checkAuthState } = useAuthStore(); useEffect(() => { let unsubscribe; if (!isAuthLoading) { // 如果persist已经恢复了用户数据,先确认Firebase本地会话是否有效 const currentFirebaseUser = auth.currentUser; if (currentFirebaseUser && !user) { // 若Firebase有用户但Store中无,同步数据 useAuthStore.setState({ user: { uid: currentFirebaseUser.uid, email: currentFirebaseUser.email, }, }); } // 启动Firebase auth状态监听 unsubscribe = checkAuthState(); } return () => unsubscribe?.(); }, [isAuthLoading]); // 根据isAuthLoading和user决定渲染内容 if (isAuthLoading) { return <Text>加载中...</Text>; } return user ? <HomeScreen /> : <AuthScreen />; }
3. 验证存储数据有效性
再次检查AsyncStorage中的存储内容,确认user-state对应的JSON数据包含正确的用户字段(如email、uid)。若存储数据为空或字段缺失,检查login方法是否正确设置了user状态。
额外注意事项
- 确保Zustand和相关依赖版本兼容:使用最新稳定版的
zustand和@react-native-async-storage/async-storage。 - Expo环境下无需额外配置AsyncStorage,但要确保已安装
@react-native-async-storage/async-storage并正确导入。
内容的提问来源于stack exchange,提问作者Guigz
相关产品推荐
相关产品推荐

