UNPKG

2.93 kBTypeScriptView Raw
1import * as React from 'react';
2import { getApps, initializeApp, registerVersion } from 'firebase/app';
3
4import type { FirebaseApp, FirebaseOptions } from 'firebase/app';
5
6// INVESTIGATE I don't like magic strings, can we have export this in js-sdk?
7const DEFAULT_APP_NAME = '[DEFAULT]';
8
9const FirebaseAppContext = React.createContext<FirebaseApp | undefined>(undefined);
10const SuspenseEnabledContext = React.createContext<boolean>(false);
11
12interface FirebaseAppProviderProps {
13 firebaseApp?: FirebaseApp;
14 firebaseConfig?: FirebaseOptions;
15 appName?: string;
16 suspense?: boolean;
17}
18
19// @ts-expect-error: "__REACTFIRE_VERSION__" is replaced with actual ReactFire version (see babel.config.js)
20export const version = __REACTFIRE_VERSION__;
21
22const shallowEq = (a: { [key: string]: any }, b: { [key: string]: any }) => a === b || [...Object.keys(a), ...Object.keys(b)].every((key) => a[key] === b[key]);
23
24export function FirebaseAppProvider(props: React.PropsWithChildren<FirebaseAppProviderProps>) {
25 const { firebaseConfig, appName, suspense } = props;
26
27 const firebaseApp: FirebaseApp = React.useMemo(() => {
28 if (props.firebaseApp) {
29 return props.firebaseApp;
30 }
31
32 const existingApp = getApps().find((app) => app.name === (appName || DEFAULT_APP_NAME));
33 if (existingApp) {
34 if (firebaseConfig && shallowEq(existingApp.options, firebaseConfig)) {
35 return existingApp;
36 } else {
37 throw new Error(
38 `Does not match the options already provided to the ${appName || 'default'} firebase app instance, give this new instance a different appName.`
39 );
40 }
41 } else {
42 if (!firebaseConfig) {
43 throw new Error('No firebaseConfig provided');
44 }
45
46 const reactVersion = React.version || 'unknown';
47 registerVersion('react', reactVersion);
48 registerVersion('reactfire', version);
49 return initializeApp(firebaseConfig, appName);
50 }
51 }, [props.firebaseApp, firebaseConfig, appName]);
52
53 return (
54 <FirebaseAppContext.Provider value={firebaseApp}>
55 <SuspenseEnabledContext.Provider value={suspense ?? false} {...props} />
56 </FirebaseAppContext.Provider>
57 );
58}
59
60export function useIsSuspenseEnabled(): boolean {
61 const suspense = React.useContext(SuspenseEnabledContext);
62
63 // default to false if not available in context
64 return suspense ?? false;
65}
66
67export function useSuspenseEnabledFromConfigAndContext(suspenseFromConfig?: boolean): boolean {
68 let suspenseFromContext = React.useContext(SuspenseEnabledContext);
69
70 // prioritize config over context
71 if (suspenseFromConfig !== undefined) {
72 return suspenseFromConfig;
73 }
74
75 return suspenseFromContext;
76}
77
78export function useFirebaseApp() {
79 const firebaseApp = React.useContext(FirebaseAppContext);
80 if (!firebaseApp) {
81 throw new Error('Cannot call useFirebaseApp unless your component is within a FirebaseAppProvider');
82 }
83
84 return firebaseApp;
85}