@umituz/react-native-firebase 1.13.28 → 1.13.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-firebase",
3
- "version": "1.13.28",
3
+ "version": "1.13.29",
4
4
  "description": "Unified Firebase package for React Native apps - Auth and Firestore services using Firebase JS SDK (no native modules).",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -35,7 +35,8 @@
35
35
  "expo-crypto": ">=13.0.0",
36
36
  "firebase": ">=10.0.0",
37
37
  "react": ">=18.2.0",
38
- "react-native": ">=0.74.0"
38
+ "react-native": ">=0.74.0",
39
+ "zustand": ">=5.0.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@react-native-async-storage/async-storage": "^2.2.0",
@@ -49,7 +50,8 @@
49
50
  "firebase": "^12.6.0",
50
51
  "react": "19.1.0",
51
52
  "react-native": "0.81.5",
52
- "typescript": "~5.9.2"
53
+ "typescript": "~5.9.2",
54
+ "zustand": "^5.0.0"
53
55
  },
54
56
  "publishConfig": {
55
57
  "access": "public"
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Firebase Auth Store
3
+ * Shared Zustand store for Firebase Auth state
4
+ *
5
+ * CRITICAL: This store ensures only ONE auth listener is created,
6
+ * preventing performance issues from multiple subscriptions.
7
+ */
8
+
9
+ import { create } from "zustand";
10
+ import { onAuthStateChanged, type User, type Auth } from "firebase/auth";
11
+
12
+ declare const __DEV__: boolean;
13
+
14
+ interface AuthState {
15
+ user: User | null;
16
+ loading: boolean;
17
+ initialized: boolean;
18
+ listenerSetup: boolean;
19
+ }
20
+
21
+ interface AuthActions {
22
+ setupListener: (auth: Auth) => void;
23
+ cleanup: () => void;
24
+ }
25
+
26
+ type AuthStore = AuthState & AuthActions;
27
+
28
+ let unsubscribe: (() => void) | null = null;
29
+
30
+ export const useAuthStore = create<AuthStore>((set, get) => ({
31
+ user: null,
32
+ loading: true,
33
+ initialized: false,
34
+ listenerSetup: false,
35
+
36
+ setupListener: (auth: Auth) => {
37
+ const state = get();
38
+
39
+ // Only setup listener once
40
+ if (state.listenerSetup || unsubscribe) {
41
+ return;
42
+ }
43
+
44
+ set({ listenerSetup: true });
45
+
46
+ unsubscribe = onAuthStateChanged(auth, (currentUser: User | null) => {
47
+ if (__DEV__) {
48
+ console.log(
49
+ "[AuthStore] Auth state changed:",
50
+ currentUser?.uid || "null"
51
+ );
52
+ }
53
+ set({
54
+ user: currentUser,
55
+ loading: false,
56
+ initialized: true,
57
+ });
58
+ });
59
+ },
60
+
61
+ cleanup: () => {
62
+ if (unsubscribe) {
63
+ unsubscribe();
64
+ unsubscribe = null;
65
+ }
66
+ set({
67
+ user: null,
68
+ loading: true,
69
+ initialized: false,
70
+ listenerSetup: false,
71
+ });
72
+ },
73
+ }));
@@ -2,15 +2,14 @@
2
2
  * useFirebaseAuth Hook
3
3
  * React hook for Firebase Auth state management
4
4
  *
5
- * Directly uses Firebase Auth's built-in state management via onAuthStateChanged
6
- * Simple, performant, no retry mechanism needed (auth is pre-initialized)
5
+ * Uses shared Zustand store to ensure only ONE auth listener exists.
6
+ * This prevents performance issues from multiple subscriptions.
7
7
  */
8
8
 
9
- import { useEffect, useState, useRef } from "react";
10
- import { onAuthStateChanged, type User } from "firebase/auth";
9
+ import { useEffect } from "react";
10
+ import type { User } from "firebase/auth";
11
11
  import { getFirebaseAuth } from "../../infrastructure/config/FirebaseAuthClient";
12
-
13
- declare const __DEV__: boolean;
12
+ import { useAuthStore } from "../../infrastructure/stores/auth.store";
14
13
 
15
14
  export interface UseFirebaseAuthResult {
16
15
  /** Current authenticated user from Firebase Auth */
@@ -24,7 +23,7 @@ export interface UseFirebaseAuthResult {
24
23
  /**
25
24
  * Hook for Firebase Auth state management
26
25
  *
27
- * Directly uses Firebase Auth's built-in state management.
26
+ * Uses shared store to ensure only one listener is active.
28
27
  * Auth is pre-initialized in appInitializer, so no retry needed.
29
28
  *
30
29
  * @example
@@ -33,41 +32,18 @@ export interface UseFirebaseAuthResult {
33
32
  * ```
34
33
  */
35
34
  export function useFirebaseAuth(): UseFirebaseAuthResult {
36
- const [user, setUser] = useState<User | null>(null);
37
- const [loading, setLoading] = useState(true);
38
- const [initialized, setInitialized] = useState(false);
39
-
40
- const unsubscribeRef = useRef<(() => void) | null>(null);
35
+ const { user, loading, initialized, setupListener } = useAuthStore();
41
36
 
42
37
  useEffect(() => {
43
38
  const auth = getFirebaseAuth();
44
39
 
45
40
  if (!auth) {
46
- // Auth not available (offline mode or error)
47
- setLoading(false);
48
- setUser(null);
49
- setInitialized(false);
50
41
  return;
51
42
  }
52
43
 
53
- // Subscribe to auth state changes
54
- unsubscribeRef.current = onAuthStateChanged(auth, (currentUser: User | null) => {
55
- if (__DEV__) {
56
- console.log('[useFirebaseAuth] Auth state changed:', currentUser?.uid || 'null');
57
- }
58
- setUser(currentUser);
59
- setLoading(false);
60
- setInitialized(true);
61
- });
62
-
63
- // Cleanup on unmount
64
- return () => {
65
- if (unsubscribeRef.current) {
66
- unsubscribeRef.current();
67
- unsubscribeRef.current = null;
68
- }
69
- };
70
- }, []); // Empty deps - subscribe once on mount
44
+ // Setup listener (will only run once due to store check)
45
+ setupListener(auth);
46
+ }, [setupListener]);
71
47
 
72
48
  return {
73
49
  user,
@@ -75,4 +51,3 @@ export function useFirebaseAuth(): UseFirebaseAuthResult {
75
51
  initialized,
76
52
  };
77
53
  }
78
-