UNPKG

6.34 kBTypeScriptView Raw
1import * as React from 'react';
2
3import type { AppCheck } from 'firebase/app-check';
4import type { Auth } from 'firebase/auth';
5import type { Analytics } from 'firebase/analytics';
6import type { Database } from 'firebase/database';
7import type { Firestore } from 'firebase/firestore';
8import type { Functions } from 'firebase/functions';
9import type { FirebasePerformance } from 'firebase/performance';
10import type { FirebaseStorage } from 'firebase/storage';
11import type { RemoteConfig } from 'firebase/remote-config';
12import { useFirebaseApp } from './firebaseApp';
13import { FirebaseApp } from 'firebase/app';
14import { ObservableStatus, useObservable } from './useObservable';
15import { from } from 'rxjs';
16import { ReactFireOptions } from '.';
17
18export const AppCheckSdkContext = React.createContext<AppCheck | undefined>(undefined);
19export const AuthSdkContext = React.createContext<Auth | undefined>(undefined);
20export const AnalyticsSdkContext = React.createContext<Analytics | undefined>(undefined);
21export const DatabaseSdkContext = React.createContext<Database | undefined>(undefined);
22export const FirestoreSdkContext = React.createContext<Firestore | undefined>(undefined);
23export const FunctionsSdkContext = React.createContext<Functions | undefined>(undefined);
24export const StorageSdkContext = React.createContext<FirebaseStorage | undefined>(undefined);
25export const PerformanceSdkContext = React.createContext<FirebasePerformance | undefined>(undefined);
26export const RemoteConfigSdkContext = React.createContext<RemoteConfig | undefined>(undefined);
27
28type FirebaseSdks = Analytics | AppCheck | Auth | Database | Firestore | FirebasePerformance | FirebaseStorage | Functions | RemoteConfig;
29
30function getSdkProvider<Sdk extends FirebaseSdks>(SdkContext: React.Context<Sdk | undefined>) {
31 return function SdkProvider(props: React.PropsWithChildren<{ sdk: Sdk }>) {
32 if (!props.sdk) throw new Error('no sdk provided');
33
34 const contextualAppName = useFirebaseApp().name;
35 const sdkAppName = props?.sdk?.app?.name;
36 if (sdkAppName !== contextualAppName) throw new Error('sdk was initialized with a different firebase app');
37
38 return <SdkContext.Provider value={props.sdk} {...props} />;
39 };
40}
41
42function useSdk<Sdk extends FirebaseSdks>(SdkContext: React.Context<Sdk | undefined>): Sdk {
43 const sdk = React.useContext(SdkContext);
44
45 if (!sdk) {
46 throw new Error('SDK not found. useSdk must be called from within a provider');
47 }
48
49 return sdk;
50}
51
52function useInitSdk<Sdk extends FirebaseSdks>(
53 sdkName: string,
54 SdkContext: React.Context<Sdk | undefined>,
55 sdkInitializer: (firebaseApp: FirebaseApp) => Promise<Sdk>,
56 options?: ReactFireOptions
57) {
58 const firebaseApp = useFirebaseApp();
59
60 // Some initialization functions (like Firestore's `enableIndexedDbPersistence`)
61 // can only be called before anything else. So if an sdk is already available in context,
62 // it isn't safe to call initialization functions again.
63 if (React.useContext(SdkContext)) {
64 throw new Error(`Cannot initialize SDK ${sdkName} because it already exists in Context`);
65 }
66
67 const initializeSdk = React.useMemo(() => sdkInitializer(firebaseApp), [firebaseApp]);
68
69 return useObservable<Sdk>(`firebase-sdk:${sdkName}:${firebaseApp.name}`, from(initializeSdk), options);
70}
71
72export const AppCheckProvider = getSdkProvider<AppCheck>(AppCheckSdkContext);
73export const AuthProvider = getSdkProvider<Auth>(AuthSdkContext);
74export const AnalyticsProvider = getSdkProvider<Analytics>(AnalyticsSdkContext);
75export const DatabaseProvider = getSdkProvider<Database>(DatabaseSdkContext);
76export const FirestoreProvider = getSdkProvider<Firestore>(FirestoreSdkContext);
77export const FunctionsProvider = getSdkProvider<Functions>(FunctionsSdkContext);
78export const PerformanceProvider = getSdkProvider<FirebasePerformance>(PerformanceSdkContext);
79export const StorageProvider = getSdkProvider<FirebaseStorage>(StorageSdkContext);
80export const RemoteConfigProvider = getSdkProvider<RemoteConfig>(RemoteConfigSdkContext);
81
82export const useAppCheck = () => useSdk<AppCheck>(AppCheckSdkContext);
83export const useAuth = () => useSdk<Auth>(AuthSdkContext);
84export const useAnalytics = () => useSdk<Analytics>(AnalyticsSdkContext);
85export const useDatabase = () => useSdk<Database>(DatabaseSdkContext);
86export const useFirestore = () => useSdk<Firestore>(FirestoreSdkContext);
87export const useFunctions = () => useSdk<Functions>(FunctionsSdkContext);
88export const usePerformance = () => useSdk<FirebasePerformance>(PerformanceSdkContext);
89export const useStorage = () => useSdk<FirebaseStorage>(StorageSdkContext);
90export const useRemoteConfig = () => useSdk<RemoteConfig>(RemoteConfigSdkContext);
91
92type InitSdkHook<Sdk extends FirebaseSdks> = (
93 initializer: (firebaseApp: FirebaseApp) => Promise<Sdk>,
94 options?: ReactFireOptions<Sdk>
95) => ObservableStatus<Sdk>;
96
97export const useInitAppCheck: InitSdkHook<AppCheck> = (initializer, options) => useInitSdk<AppCheck>('appcheck', AppCheckSdkContext, initializer, options);
98export const useInitAuth: InitSdkHook<Auth> = (initializer, options) => useInitSdk<Auth>('auth', AuthSdkContext, initializer, options);
99export const useInitAnalytics: InitSdkHook<Analytics> = (initializer, options) => useInitSdk<Analytics>('analytics', AnalyticsSdkContext, initializer, options);
100export const useInitDatabase: InitSdkHook<Database> = (initializer, options) => useInitSdk<Database>('database', DatabaseSdkContext, initializer, options);
101export const useInitFirestore: InitSdkHook<Firestore> = (initializer, options) => useInitSdk<Firestore>('firestore', FirestoreSdkContext, initializer, options);
102export const useInitFunctions: InitSdkHook<Functions> = (initializer, options) => useInitSdk<Functions>('functions', FunctionsSdkContext, initializer, options);
103export const useInitPerformance: InitSdkHook<FirebasePerformance> = (initializer, options) =>
104 useInitSdk<FirebasePerformance>('performance', PerformanceSdkContext, initializer, options);
105export const useInitRemoteConfig: InitSdkHook<RemoteConfig> = (initializer, options) =>
106 useInitSdk<RemoteConfig>('remoteconfig', RemoteConfigSdkContext, initializer, options);
107export const useInitStorage: InitSdkHook<FirebaseStorage> = (initializer, options) =>
108 useInitSdk<FirebaseStorage>('storage', StorageSdkContext, initializer, options);