react-query-firebase 2.5.0 → 2.6.1
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 +1 -1
- package/react-native/context/FirebaseContextProvider.js +17 -17
- package/react-native/context/FirebaseContextProvider.tsx +23 -18
- package/react-native/firestore/index.d.ts +1 -0
- package/react-native/firestore/index.js +1 -0
- package/react-native/firestore/index.ts +1 -0
- package/react-native/firestore/useEnsureDoc.d.ts +40 -0
- package/react-native/firestore/useEnsureDoc.js +47 -0
- package/react-native/firestore/useEnsureDoc.ts +79 -0
- package/web/firestore/index.d.ts +1 -0
- package/web/firestore/index.js +1 -0
- package/web/firestore/index.ts +1 -0
- package/web/firestore/useEnsureDoc.d.ts +40 -0
- package/web/firestore/useEnsureDoc.js +47 -0
- package/web/firestore/useEnsureDoc.ts +79 -0
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { useEffect, useMemo } from "react";
|
|
2
2
|
import { FirebaseContext } from "./FirebaseContext";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
3
|
+
import { connectAuthEmulator, getAuth } from "@react-native-firebase/auth";
|
|
4
|
+
import { setAnalyticsCollectionEnabled, setConsent, getAnalytics } from "@react-native-firebase/analytics";
|
|
5
|
+
import { getRemoteConfig } from "@react-native-firebase/remote-config";
|
|
6
|
+
import { connectFirestoreEmulator, getFirestore } from "@react-native-firebase/firestore";
|
|
7
|
+
import { getApp } from "@react-native-firebase/app";
|
|
8
8
|
/**
|
|
9
9
|
* FirebaseContextProvider component configures and provides Firebase services to its children.
|
|
10
10
|
* Initializes Firebase app and enables optional Firebase services such as Firestore, Auth, Analytics,
|
|
@@ -29,9 +29,9 @@ import firebase from "@react-native-firebase/app";
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
export const FirebaseContextProvider = ({ emulators, children, authEnabled = true, firestoreEnabled = true, analyticsEnabled = true, consentSettings = {}, remoteConfigEnabled = true, remoteConfigSettings, remoteConfigDefaults = {}, firestoreSettings }) => {
|
|
32
|
-
const internalFirebase = useMemo(() =>
|
|
32
|
+
const internalFirebase = useMemo(() => getApp(), []);
|
|
33
33
|
useEffect(() => {
|
|
34
|
-
setConsent(
|
|
34
|
+
setConsent(getAnalytics(internalFirebase), {
|
|
35
35
|
ad_personalization: false,
|
|
36
36
|
ad_storage: false,
|
|
37
37
|
ad_user_data: false,
|
|
@@ -41,23 +41,23 @@ export const FirebaseContextProvider = ({ emulators, children, authEnabled = tru
|
|
|
41
41
|
security_storage: false,
|
|
42
42
|
...consentSettings
|
|
43
43
|
});
|
|
44
|
-
}, [consentSettings]);
|
|
44
|
+
}, [consentSettings, internalFirebase]);
|
|
45
45
|
const internalFirestore = useMemo(() => {
|
|
46
46
|
if (firestoreEnabled) {
|
|
47
47
|
if (emulators?.firestore?.host && emulators?.firestore?.port) {
|
|
48
|
-
connectFirestoreEmulator(
|
|
48
|
+
connectFirestoreEmulator(getFirestore(internalFirebase), emulators.firestore.host, emulators.firestore.port);
|
|
49
49
|
}
|
|
50
|
-
const localFirestore =
|
|
50
|
+
const localFirestore = getFirestore(internalFirebase);
|
|
51
51
|
if (firestoreSettings) {
|
|
52
52
|
localFirestore.settings(firestoreSettings);
|
|
53
53
|
}
|
|
54
54
|
return localFirestore;
|
|
55
55
|
}
|
|
56
56
|
return null;
|
|
57
|
-
}, [emulators?.firestore, firestoreEnabled, firestoreSettings]);
|
|
57
|
+
}, [emulators?.firestore, firestoreEnabled, internalFirebase, firestoreSettings]);
|
|
58
58
|
const internalAuth = useMemo(() => {
|
|
59
59
|
if (authEnabled) {
|
|
60
|
-
const localAuth =
|
|
60
|
+
const localAuth = getAuth(internalFirebase);
|
|
61
61
|
if (emulators?.auth?.host) {
|
|
62
62
|
connectAuthEmulator(localAuth, emulators?.auth?.host, {
|
|
63
63
|
disableWarnings: true
|
|
@@ -66,16 +66,16 @@ export const FirebaseContextProvider = ({ emulators, children, authEnabled = tru
|
|
|
66
66
|
return localAuth;
|
|
67
67
|
}
|
|
68
68
|
return null;
|
|
69
|
-
}, [emulators?.auth, authEnabled]);
|
|
69
|
+
}, [emulators?.auth, authEnabled, internalFirebase]);
|
|
70
70
|
const internalAnalytics = useMemo(() => {
|
|
71
71
|
if (analyticsEnabled) {
|
|
72
|
-
return
|
|
72
|
+
return getAnalytics(internalFirebase);
|
|
73
73
|
}
|
|
74
74
|
return null;
|
|
75
|
-
}, [analyticsEnabled]);
|
|
75
|
+
}, [analyticsEnabled, internalFirebase]);
|
|
76
76
|
const internalRemoteConfig = useMemo(() => {
|
|
77
77
|
if (remoteConfigEnabled) {
|
|
78
|
-
const localRemoteConfig =
|
|
78
|
+
const localRemoteConfig = getRemoteConfig(internalFirebase);
|
|
79
79
|
if (remoteConfigSettings) {
|
|
80
80
|
localRemoteConfig.settings.fetchTimeMillis = remoteConfigSettings.fetchTimeMillis;
|
|
81
81
|
localRemoteConfig.settings.minimumFetchIntervalMillis = remoteConfigSettings.minimumFetchIntervalMillis;
|
|
@@ -84,7 +84,7 @@ export const FirebaseContextProvider = ({ emulators, children, authEnabled = tru
|
|
|
84
84
|
return localRemoteConfig;
|
|
85
85
|
}
|
|
86
86
|
return null;
|
|
87
|
-
}, [remoteConfigEnabled, remoteConfigSettings, remoteConfigDefaults]);
|
|
87
|
+
}, [remoteConfigEnabled, remoteConfigSettings, remoteConfigDefaults, internalFirebase]);
|
|
88
88
|
const contextValue = useMemo(() => ({
|
|
89
89
|
firebase: internalFirebase,
|
|
90
90
|
auth: internalAuth,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useEffect, useMemo } from "react";
|
|
2
2
|
import { FirebaseContext, FirebaseContextValue } from "./FirebaseContext";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import { connectAuthEmulator, getAuth } from "@react-native-firebase/auth";
|
|
4
|
+
import {
|
|
5
5
|
FirebaseAnalyticsTypes,
|
|
6
6
|
setAnalyticsCollectionEnabled,
|
|
7
|
-
setConsent
|
|
7
|
+
setConsent,
|
|
8
|
+
getAnalytics
|
|
8
9
|
} from "@react-native-firebase/analytics";
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import { FirebaseRemoteConfigTypes, getRemoteConfig } from "@react-native-firebase/remote-config";
|
|
11
|
+
import { connectFirestoreEmulator, getFirestore } from "@react-native-firebase/firestore";
|
|
12
|
+
import { ReactNativeFirebase, getApp } from "@react-native-firebase/app";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @inline
|
|
@@ -164,10 +165,10 @@ export const FirebaseContextProvider: React.FC<FirebaseContextProviderProps> = (
|
|
|
164
165
|
remoteConfigDefaults = {},
|
|
165
166
|
firestoreSettings
|
|
166
167
|
}) => {
|
|
167
|
-
const internalFirebase = useMemo(() =>
|
|
168
|
+
const internalFirebase = useMemo(() => getApp(), []);
|
|
168
169
|
|
|
169
170
|
useEffect(() => {
|
|
170
|
-
setConsent(
|
|
171
|
+
setConsent(getAnalytics(internalFirebase), {
|
|
171
172
|
ad_personalization: false,
|
|
172
173
|
ad_storage: false,
|
|
173
174
|
ad_user_data: false,
|
|
@@ -177,15 +178,19 @@ export const FirebaseContextProvider: React.FC<FirebaseContextProviderProps> = (
|
|
|
177
178
|
security_storage: false,
|
|
178
179
|
...consentSettings
|
|
179
180
|
});
|
|
180
|
-
}, [consentSettings]);
|
|
181
|
+
}, [consentSettings, internalFirebase]);
|
|
181
182
|
|
|
182
183
|
const internalFirestore = useMemo(() => {
|
|
183
184
|
if (firestoreEnabled) {
|
|
184
185
|
if (emulators?.firestore?.host && emulators?.firestore?.port) {
|
|
185
|
-
connectFirestoreEmulator(
|
|
186
|
+
connectFirestoreEmulator(
|
|
187
|
+
getFirestore(internalFirebase),
|
|
188
|
+
emulators.firestore.host,
|
|
189
|
+
emulators.firestore.port
|
|
190
|
+
);
|
|
186
191
|
}
|
|
187
192
|
|
|
188
|
-
const localFirestore =
|
|
193
|
+
const localFirestore = getFirestore(internalFirebase);
|
|
189
194
|
if (firestoreSettings) {
|
|
190
195
|
localFirestore.settings(firestoreSettings);
|
|
191
196
|
}
|
|
@@ -193,11 +198,11 @@ export const FirebaseContextProvider: React.FC<FirebaseContextProviderProps> = (
|
|
|
193
198
|
}
|
|
194
199
|
|
|
195
200
|
return null;
|
|
196
|
-
}, [emulators?.firestore, firestoreEnabled, firestoreSettings]);
|
|
201
|
+
}, [emulators?.firestore, firestoreEnabled, internalFirebase, firestoreSettings]);
|
|
197
202
|
|
|
198
203
|
const internalAuth = useMemo(() => {
|
|
199
204
|
if (authEnabled) {
|
|
200
|
-
const localAuth =
|
|
205
|
+
const localAuth = getAuth(internalFirebase);
|
|
201
206
|
if (emulators?.auth?.host) {
|
|
202
207
|
connectAuthEmulator(localAuth, emulators?.auth?.host, {
|
|
203
208
|
disableWarnings: true
|
|
@@ -206,18 +211,18 @@ export const FirebaseContextProvider: React.FC<FirebaseContextProviderProps> = (
|
|
|
206
211
|
return localAuth;
|
|
207
212
|
}
|
|
208
213
|
return null;
|
|
209
|
-
}, [emulators?.auth, authEnabled]);
|
|
214
|
+
}, [emulators?.auth, authEnabled, internalFirebase]);
|
|
210
215
|
|
|
211
216
|
const internalAnalytics = useMemo(() => {
|
|
212
217
|
if (analyticsEnabled) {
|
|
213
|
-
return
|
|
218
|
+
return getAnalytics(internalFirebase);
|
|
214
219
|
}
|
|
215
220
|
return null;
|
|
216
|
-
}, [analyticsEnabled]);
|
|
221
|
+
}, [analyticsEnabled, internalFirebase]);
|
|
217
222
|
|
|
218
223
|
const internalRemoteConfig = useMemo(() => {
|
|
219
224
|
if (remoteConfigEnabled) {
|
|
220
|
-
const localRemoteConfig =
|
|
225
|
+
const localRemoteConfig = getRemoteConfig(internalFirebase);
|
|
221
226
|
if (remoteConfigSettings) {
|
|
222
227
|
localRemoteConfig.settings.fetchTimeMillis = remoteConfigSettings.fetchTimeMillis;
|
|
223
228
|
localRemoteConfig.settings.minimumFetchIntervalMillis = remoteConfigSettings.minimumFetchIntervalMillis;
|
|
@@ -226,7 +231,7 @@ export const FirebaseContextProvider: React.FC<FirebaseContextProviderProps> = (
|
|
|
226
231
|
return localRemoteConfig;
|
|
227
232
|
}
|
|
228
233
|
return null;
|
|
229
|
-
}, [remoteConfigEnabled, remoteConfigSettings, remoteConfigDefaults]);
|
|
234
|
+
}, [remoteConfigEnabled, remoteConfigSettings, remoteConfigDefaults, internalFirebase]);
|
|
230
235
|
|
|
231
236
|
const contextValue = useMemo(
|
|
232
237
|
() => ({
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { UseQueryOptions } from "@tanstack/react-query";
|
|
2
|
+
import { AppModel } from "../../types";
|
|
3
|
+
import { GetDocDataOptions } from "./utils/getDocData";
|
|
4
|
+
/**
|
|
5
|
+
* @inline
|
|
6
|
+
*/
|
|
7
|
+
export type UseEnsureDocOptions<AppModelType extends AppModel = AppModel> = {
|
|
8
|
+
/**
|
|
9
|
+
* Reference to a document that must be written
|
|
10
|
+
*/
|
|
11
|
+
defaults: AppModelType;
|
|
12
|
+
/**
|
|
13
|
+
* Options for useMutation hook excluding mutationFn.
|
|
14
|
+
*/
|
|
15
|
+
options: Omit<UseQueryOptions<AppModelType, Error, AppModelType>, "queryFn"> & Required<Pick<UseQueryOptions<AppModelType, Error, AppModelType>, "queryKey">>;
|
|
16
|
+
} & Omit<GetDocDataOptions<AppModelType>, "db">;
|
|
17
|
+
/**
|
|
18
|
+
* This hook checks if a doc with a requested reference exists.
|
|
19
|
+
* It creates a document with requested data if it does not exist.
|
|
20
|
+
*
|
|
21
|
+
* @group Hook
|
|
22
|
+
*
|
|
23
|
+
* @param {UseEnsureDocOptions<AppModelType>} options - Configuration options for mutation.
|
|
24
|
+
*
|
|
25
|
+
* @returns {UseQueryResult<AppModelType, Error>} A mutation result
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```jsx
|
|
29
|
+
* export const MyComponent = () => {
|
|
30
|
+
* const {data} = useEnsureDocQuery({
|
|
31
|
+
* options: {
|
|
32
|
+
* },
|
|
33
|
+
* reference: collection().doc(),
|
|
34
|
+
* defaults: {prop1: 'value1'}
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const useEnsureDoc: <AppModelType extends AppModel = AppModel>({ reference, path, pathSegments, defaults, options }: UseEnsureDocOptions<AppModelType>) => import("@tanstack/react-query").UseQueryResult<AppModelType, Error>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { getDoc, setDoc } from "@react-native-firebase/firestore";
|
|
3
|
+
import { getDocRef } from "./utils/getDocRef";
|
|
4
|
+
import { getDocSnap } from "./utils/getDocSnap";
|
|
5
|
+
import { useFirestore } from "./useFirestore";
|
|
6
|
+
/**
|
|
7
|
+
* This hook checks if a doc with a requested reference exists.
|
|
8
|
+
* It creates a document with requested data if it does not exist.
|
|
9
|
+
*
|
|
10
|
+
* @group Hook
|
|
11
|
+
*
|
|
12
|
+
* @param {UseEnsureDocOptions<AppModelType>} options - Configuration options for mutation.
|
|
13
|
+
*
|
|
14
|
+
* @returns {UseQueryResult<AppModelType, Error>} A mutation result
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```jsx
|
|
18
|
+
* export const MyComponent = () => {
|
|
19
|
+
* const {data} = useEnsureDocQuery({
|
|
20
|
+
* options: {
|
|
21
|
+
* },
|
|
22
|
+
* reference: collection().doc(),
|
|
23
|
+
* defaults: {prop1: 'value1'}
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* };
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export const useEnsureDoc = ({ reference, path, pathSegments, defaults, options }) => {
|
|
30
|
+
const db = useFirestore();
|
|
31
|
+
return useQuery({
|
|
32
|
+
...options,
|
|
33
|
+
queryFn: async () => {
|
|
34
|
+
const existingDocSnap = await getDocSnap({ db, path, pathSegments, reference });
|
|
35
|
+
if (existingDocSnap?.exists) {
|
|
36
|
+
return { ...existingDocSnap.data(), uid: existingDocSnap.id };
|
|
37
|
+
}
|
|
38
|
+
const docRef = getDocRef({ db, reference, path, pathSegments });
|
|
39
|
+
if (!docRef) {
|
|
40
|
+
throw new Error(`Cannot fetch document reference using data: ${reference?.path}, ${path}, ${pathSegments?.join("/")}`);
|
|
41
|
+
}
|
|
42
|
+
await setDoc(docRef, defaults);
|
|
43
|
+
const docSnap = await getDoc(docRef);
|
|
44
|
+
return { ...docSnap.data(), uid: docSnap.id };
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
|
2
|
+
import { getDoc, setDoc } from "@react-native-firebase/firestore";
|
|
3
|
+
|
|
4
|
+
import { AppModel } from "../../types";
|
|
5
|
+
import { GetDocDataOptions } from "./utils/getDocData";
|
|
6
|
+
import { getDocRef } from "./utils/getDocRef";
|
|
7
|
+
import { getDocSnap } from "./utils/getDocSnap";
|
|
8
|
+
import { useFirestore } from "./useFirestore";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @inline
|
|
12
|
+
*/
|
|
13
|
+
export type UseEnsureDocOptions<AppModelType extends AppModel = AppModel> = {
|
|
14
|
+
/**
|
|
15
|
+
* Reference to a document that must be written
|
|
16
|
+
*/
|
|
17
|
+
defaults: AppModelType;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Options for useMutation hook excluding mutationFn.
|
|
21
|
+
*/
|
|
22
|
+
options: Omit<UseQueryOptions<AppModelType, Error, AppModelType>, "queryFn"> &
|
|
23
|
+
Required<Pick<UseQueryOptions<AppModelType, Error, AppModelType>, "queryKey">>;
|
|
24
|
+
} & Omit<GetDocDataOptions<AppModelType>, "db">;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* This hook checks if a doc with a requested reference exists.
|
|
28
|
+
* It creates a document with requested data if it does not exist.
|
|
29
|
+
*
|
|
30
|
+
* @group Hook
|
|
31
|
+
*
|
|
32
|
+
* @param {UseEnsureDocOptions<AppModelType>} options - Configuration options for mutation.
|
|
33
|
+
*
|
|
34
|
+
* @returns {UseQueryResult<AppModelType, Error>} A mutation result
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```jsx
|
|
38
|
+
* export const MyComponent = () => {
|
|
39
|
+
* const {data} = useEnsureDocQuery({
|
|
40
|
+
* options: {
|
|
41
|
+
* },
|
|
42
|
+
* reference: collection().doc(),
|
|
43
|
+
* defaults: {prop1: 'value1'}
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* };
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const useEnsureDoc = <AppModelType extends AppModel = AppModel>({
|
|
50
|
+
reference,
|
|
51
|
+
path,
|
|
52
|
+
pathSegments,
|
|
53
|
+
defaults,
|
|
54
|
+
options
|
|
55
|
+
}: UseEnsureDocOptions<AppModelType>) => {
|
|
56
|
+
const db = useFirestore();
|
|
57
|
+
|
|
58
|
+
return useQuery({
|
|
59
|
+
...options,
|
|
60
|
+
queryFn: async () => {
|
|
61
|
+
const existingDocSnap = await getDocSnap({ db, path, pathSegments, reference });
|
|
62
|
+
|
|
63
|
+
if (existingDocSnap?.exists) {
|
|
64
|
+
return { ...(existingDocSnap.data() as AppModelType), uid: existingDocSnap.id };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const docRef = getDocRef({ db, reference, path, pathSegments });
|
|
68
|
+
if (!docRef) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Cannot fetch document reference using data: ${reference?.path}, ${path}, ${pathSegments?.join("/")}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await setDoc<AppModelType>(docRef, defaults);
|
|
75
|
+
const docSnap = await getDoc(docRef);
|
|
76
|
+
return { ...(docSnap.data() as AppModelType), uid: docSnap.id };
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
};
|
package/web/firestore/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./useSetDocMutation";
|
|
|
15
15
|
export * from "./useUpdateDocMutation";
|
|
16
16
|
export * from "./useGetRealtimeDocData";
|
|
17
17
|
export * from "./useQueryConstraints";
|
|
18
|
+
export * from "./useEnsureDoc";
|
|
18
19
|
export * from "./utils/getDocData";
|
|
19
20
|
export * from "./utils/getDocSnap";
|
|
20
21
|
export * from "./utils/getDocRef";
|
package/web/firestore/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./useSetDocMutation";
|
|
|
15
15
|
export * from "./useUpdateDocMutation";
|
|
16
16
|
export * from "./useGetRealtimeDocData";
|
|
17
17
|
export * from "./useQueryConstraints";
|
|
18
|
+
export * from "./useEnsureDoc";
|
|
18
19
|
export * from "./utils/getDocData";
|
|
19
20
|
export * from "./utils/getDocSnap";
|
|
20
21
|
export * from "./utils/getDocRef";
|
package/web/firestore/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./useSetDocMutation";
|
|
|
15
15
|
export * from "./useUpdateDocMutation";
|
|
16
16
|
export * from "./useGetRealtimeDocData";
|
|
17
17
|
export * from "./useQueryConstraints";
|
|
18
|
+
export * from "./useEnsureDoc";
|
|
18
19
|
export * from "./utils/getDocData";
|
|
19
20
|
export * from "./utils/getDocSnap";
|
|
20
21
|
export * from "./utils/getDocRef";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { UseQueryOptions } from "@tanstack/react-query";
|
|
2
|
+
import { AppModel } from "../../types";
|
|
3
|
+
import { GetDocDataOptions } from "./utils/getDocData";
|
|
4
|
+
/**
|
|
5
|
+
* @inline
|
|
6
|
+
*/
|
|
7
|
+
export type UseEnsureDocOptions<AppModelType extends AppModel = AppModel> = {
|
|
8
|
+
/**
|
|
9
|
+
* Reference to a document that must be written
|
|
10
|
+
*/
|
|
11
|
+
defaults: AppModelType;
|
|
12
|
+
/**
|
|
13
|
+
* Options for useMutation hook excluding mutationFn.
|
|
14
|
+
*/
|
|
15
|
+
options: Omit<UseQueryOptions<AppModelType, Error, AppModelType>, "queryFn"> & Required<Pick<UseQueryOptions<AppModelType, Error, AppModelType>, "queryKey">>;
|
|
16
|
+
} & Omit<GetDocDataOptions<AppModelType>, "db">;
|
|
17
|
+
/**
|
|
18
|
+
* This hook checks if a doc with a requested reference exists.
|
|
19
|
+
* It creates a document with requested data if it does not exist.
|
|
20
|
+
*
|
|
21
|
+
* @group Hook
|
|
22
|
+
*
|
|
23
|
+
* @param {UseEnsureDocOptions<AppModelType>} options - Configuration options for mutation.
|
|
24
|
+
*
|
|
25
|
+
* @returns {UseQueryResult<AppModelType, Error>} A mutation result
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```jsx
|
|
29
|
+
* export const MyComponent = () => {
|
|
30
|
+
* const {data} = useEnsureDocQuery({
|
|
31
|
+
* options: {
|
|
32
|
+
* },
|
|
33
|
+
* reference: collection().doc(),
|
|
34
|
+
* defaults: {prop1: 'value1'}
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const useEnsureDoc: <AppModelType extends AppModel = AppModel>({ reference, path, pathSegments, defaults, options }: UseEnsureDocOptions<AppModelType>) => import("@tanstack/react-query").UseQueryResult<AppModelType, Error>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { getDoc, setDoc } from "firebase/firestore";
|
|
3
|
+
import { getDocRef } from "./utils/getDocRef";
|
|
4
|
+
import { getDocSnap } from "./utils/getDocSnap";
|
|
5
|
+
import { useFirestore } from "./useFirestore";
|
|
6
|
+
/**
|
|
7
|
+
* This hook checks if a doc with a requested reference exists.
|
|
8
|
+
* It creates a document with requested data if it does not exist.
|
|
9
|
+
*
|
|
10
|
+
* @group Hook
|
|
11
|
+
*
|
|
12
|
+
* @param {UseEnsureDocOptions<AppModelType>} options - Configuration options for mutation.
|
|
13
|
+
*
|
|
14
|
+
* @returns {UseQueryResult<AppModelType, Error>} A mutation result
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```jsx
|
|
18
|
+
* export const MyComponent = () => {
|
|
19
|
+
* const {data} = useEnsureDocQuery({
|
|
20
|
+
* options: {
|
|
21
|
+
* },
|
|
22
|
+
* reference: collection().doc(),
|
|
23
|
+
* defaults: {prop1: 'value1'}
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* };
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export const useEnsureDoc = ({ reference, path, pathSegments, defaults, options }) => {
|
|
30
|
+
const db = useFirestore();
|
|
31
|
+
return useQuery({
|
|
32
|
+
...options,
|
|
33
|
+
queryFn: async () => {
|
|
34
|
+
const existingDocSnap = await getDocSnap({ db, path, pathSegments, reference });
|
|
35
|
+
if (existingDocSnap?.exists) {
|
|
36
|
+
return { ...existingDocSnap.data(), uid: existingDocSnap.id };
|
|
37
|
+
}
|
|
38
|
+
const docRef = getDocRef({ db, reference, path, pathSegments });
|
|
39
|
+
if (!docRef) {
|
|
40
|
+
throw new Error(`Cannot fetch document reference using data: ${reference?.path}, ${path}, ${pathSegments?.join("/")}`);
|
|
41
|
+
}
|
|
42
|
+
await setDoc(docRef, defaults);
|
|
43
|
+
const docSnap = await getDoc(docRef);
|
|
44
|
+
return { ...docSnap.data(), uid: docSnap.id };
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
|
2
|
+
import { getDoc, setDoc } from "firebase/firestore";
|
|
3
|
+
|
|
4
|
+
import { AppModel } from "../../types";
|
|
5
|
+
import { GetDocDataOptions } from "./utils/getDocData";
|
|
6
|
+
import { getDocRef } from "./utils/getDocRef";
|
|
7
|
+
import { getDocSnap } from "./utils/getDocSnap";
|
|
8
|
+
import { useFirestore } from "./useFirestore";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @inline
|
|
12
|
+
*/
|
|
13
|
+
export type UseEnsureDocOptions<AppModelType extends AppModel = AppModel> = {
|
|
14
|
+
/**
|
|
15
|
+
* Reference to a document that must be written
|
|
16
|
+
*/
|
|
17
|
+
defaults: AppModelType;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Options for useMutation hook excluding mutationFn.
|
|
21
|
+
*/
|
|
22
|
+
options: Omit<UseQueryOptions<AppModelType, Error, AppModelType>, "queryFn"> &
|
|
23
|
+
Required<Pick<UseQueryOptions<AppModelType, Error, AppModelType>, "queryKey">>;
|
|
24
|
+
} & Omit<GetDocDataOptions<AppModelType>, "db">;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* This hook checks if a doc with a requested reference exists.
|
|
28
|
+
* It creates a document with requested data if it does not exist.
|
|
29
|
+
*
|
|
30
|
+
* @group Hook
|
|
31
|
+
*
|
|
32
|
+
* @param {UseEnsureDocOptions<AppModelType>} options - Configuration options for mutation.
|
|
33
|
+
*
|
|
34
|
+
* @returns {UseQueryResult<AppModelType, Error>} A mutation result
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```jsx
|
|
38
|
+
* export const MyComponent = () => {
|
|
39
|
+
* const {data} = useEnsureDocQuery({
|
|
40
|
+
* options: {
|
|
41
|
+
* },
|
|
42
|
+
* reference: collection().doc(),
|
|
43
|
+
* defaults: {prop1: 'value1'}
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* };
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const useEnsureDoc = <AppModelType extends AppModel = AppModel>({
|
|
50
|
+
reference,
|
|
51
|
+
path,
|
|
52
|
+
pathSegments,
|
|
53
|
+
defaults,
|
|
54
|
+
options
|
|
55
|
+
}: UseEnsureDocOptions<AppModelType>) => {
|
|
56
|
+
const db = useFirestore();
|
|
57
|
+
|
|
58
|
+
return useQuery({
|
|
59
|
+
...options,
|
|
60
|
+
queryFn: async () => {
|
|
61
|
+
const existingDocSnap = await getDocSnap({ db, path, pathSegments, reference });
|
|
62
|
+
|
|
63
|
+
if (existingDocSnap?.exists) {
|
|
64
|
+
return { ...(existingDocSnap.data() as AppModelType), uid: existingDocSnap.id };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const docRef = getDocRef({ db, reference, path, pathSegments });
|
|
68
|
+
if (!docRef) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Cannot fetch document reference using data: ${reference?.path}, ${path}, ${pathSegments?.join("/")}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await setDoc<AppModelType, AppModelType>(docRef, defaults);
|
|
75
|
+
const docSnap = await getDoc(docRef);
|
|
76
|
+
return { ...(docSnap.data() as AppModelType), uid: docSnap.id };
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
};
|