You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Zustand Persist中间件在React Native认证Store中无法正常工作

问题原因分析

  1. Firebase User对象不可序列化:直接存储Firebase返回的User实例会导致序列化/反序列化丢失数据或生成无效对象,因为User包含不可JSON化的属性(如内部方法、循环引用)。
  2. 状态覆盖问题:应用重启时,checkAuthState可能在Zustand persist完成状态恢复前执行,若Firebase此时未返回用户,会将user设为null,覆盖persist存储的数据。
  3. 异步恢复时机: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 23:47:42