1 | import * as React from 'react';
|
2 | import { getApps, initializeApp, registerVersion } from 'firebase/app';
|
3 |
|
4 | import type { FirebaseApp, FirebaseOptions } from 'firebase/app';
|
5 |
|
6 |
|
7 | const DEFAULT_APP_NAME = '[DEFAULT]';
|
8 |
|
9 | const FirebaseAppContext = React.createContext<FirebaseApp | undefined>(undefined);
|
10 | const SuspenseEnabledContext = React.createContext<boolean>(false);
|
11 |
|
12 | interface FirebaseAppProviderProps {
|
13 | firebaseApp?: FirebaseApp;
|
14 | firebaseConfig?: FirebaseOptions;
|
15 | appName?: string;
|
16 | suspense?: boolean;
|
17 | }
|
18 |
|
19 |
|
20 | export const version = __REACTFIRE_VERSION__;
|
21 |
|
22 | const shallowEq = (a: { [key: string]: any }, b: { [key: string]: any }) => a === b || [...Object.keys(a), ...Object.keys(b)].every((key) => a[key] === b[key]);
|
23 |
|
24 | export 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 |
|
60 | export function useIsSuspenseEnabled(): boolean {
|
61 | const suspense = React.useContext(SuspenseEnabledContext);
|
62 |
|
63 |
|
64 | return suspense ?? false;
|
65 | }
|
66 |
|
67 | export function useSuspenseEnabledFromConfigAndContext(suspenseFromConfig?: boolean): boolean {
|
68 | let suspenseFromContext = React.useContext(SuspenseEnabledContext);
|
69 |
|
70 |
|
71 | if (suspenseFromConfig !== undefined) {
|
72 | return suspenseFromConfig;
|
73 | }
|
74 |
|
75 | return suspenseFromContext;
|
76 | }
|
77 |
|
78 | export 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 | }
|