-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: useQueries & useQueriesData (#199)
- Loading branch information
1 parent
6e5be73
commit e3a832d
Showing
13 changed files
with
724 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Query, QuerySnapshot } from "firebase/firestore"; | ||
import { describe, expectTypeOf, it } from "vitest"; | ||
import { useQueries } from "./useQueries"; | ||
|
||
describe("useQueries", () => { | ||
it("single query", () => { | ||
type Value = { key: "value" }; | ||
const query = null as unknown as Query<Value>; | ||
|
||
const results = useQueries([query]); | ||
|
||
expectTypeOf<(typeof results)[0][0]>().toMatchTypeOf<QuerySnapshot<Value> | undefined>(); | ||
}); | ||
|
||
it("multiple queries", () => { | ||
type Value1 = { key: "value" }; | ||
type Value2 = { key: "value2" }; | ||
const query1 = null as unknown as Query<Value1>; | ||
const query2 = null as unknown as Query<Value2>; | ||
|
||
const results = useQueries([query1, query2] as const); | ||
|
||
expectTypeOf<(typeof results)[0][0]>().toMatchTypeOf<QuerySnapshot<Value1> | undefined>(); | ||
expectTypeOf<(typeof results)[1][0]>().toMatchTypeOf<QuerySnapshot<Value2> | undefined>(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { DocumentData, FirestoreError, onSnapshot, Query, QuerySnapshot, SnapshotListenOptions } from "firebase/firestore"; | ||
import { useCallback } from "react"; | ||
import { ValueHookResult } from "../common/types.js"; | ||
import { useMultiListen, UseMultiListenChange } from "../internal/useMultiListen.js"; | ||
import { isQueryEqual } from "./internal.js"; | ||
|
||
export type UseQueriesResult<Values extends ReadonlyArray<DocumentData> = ReadonlyArray<DocumentData>> = { | ||
[Index in keyof Values]: ValueHookResult<QuerySnapshot<Values[Index]>, FirestoreError>; | ||
} & { length: Values["length"] }; | ||
|
||
/** | ||
* Options to configure the subscription | ||
*/ | ||
export interface UseQueriesOptions { | ||
snapshotListenOptions?: SnapshotListenOptions; | ||
} | ||
|
||
/** | ||
* Returns and updates a QuerySnapshot of multiple Firestore queries | ||
* | ||
* @template Values Tuple of types of the collection data | ||
* @param {Query[]} queries Firestore queries that will be subscribed to | ||
* @param {?UseQueriesOptions} options Options to configure the subscription | ||
* @returns {ValueHookResult[]} Array with tuple for each query: | ||
* * value: QuerySnapshot; `undefined` if query is currently being fetched, or an error occurred | ||
* * loading: `true` while fetching the query; `false` if the query was fetched successfully or an error occurred | ||
* * error: `undefined` if no error occurred | ||
*/ | ||
export function useQueries<Values extends ReadonlyArray<DocumentData> = ReadonlyArray<DocumentData>>( | ||
queries: { [Index in keyof Values]: Query<Values[Index]> }, | ||
options?: UseQueriesOptions | ||
): UseQueriesResult<Values> { | ||
const { snapshotListenOptions = {} } = options ?? {}; | ||
const onChange: UseMultiListenChange<QuerySnapshot<Values[number]>, FirestoreError, Query<Values[number]>> = useCallback( | ||
(query, next, error) => | ||
onSnapshot(query, snapshotListenOptions, { | ||
next, | ||
error, | ||
}), | ||
[] | ||
); | ||
|
||
// @ts-expect-error `useMultiListen` assumes a single value type | ||
return useMultiListen(queries, onChange, isQueryEqual); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Query } from "firebase/firestore"; | ||
import { describe, expectTypeOf, it } from "vitest"; | ||
import { useQueriesData } from "./useQueriesData"; | ||
|
||
describe("useQueriesData", () => { | ||
it("single query", () => { | ||
type Value = { key: "value" }; | ||
const query = null as unknown as Query<Value>; | ||
|
||
const results = useQueriesData([query]); | ||
|
||
expectTypeOf<(typeof results)[0][0]>().toMatchTypeOf<Value | undefined>(); | ||
}); | ||
|
||
it("multiple queries", () => { | ||
type Value1 = { key: "value" }; | ||
type Value2 = { key: "value2" }; | ||
const query1 = null as unknown as Query<Value1>; | ||
const query2 = null as unknown as Query<Value2>; | ||
|
||
const results = useQueriesData([query1, query2] as const); | ||
|
||
expectTypeOf<(typeof results)[0][0]>().toMatchTypeOf<Value1 | undefined>(); | ||
expectTypeOf<(typeof results)[1][0]>().toMatchTypeOf<Value2 | undefined>(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { DocumentData, FirestoreError, onSnapshot, Query, SnapshotListenOptions, SnapshotOptions } from "firebase/firestore"; | ||
import { useCallback } from "react"; | ||
import { ValueHookResult } from "../common/types.js"; | ||
import { useMultiListen, UseMultiListenChange } from "../internal/useMultiListen.js"; | ||
import { isQueryEqual } from "./internal.js"; | ||
|
||
export type UseQueriesDataResult<Values extends ReadonlyArray<DocumentData> = ReadonlyArray<DocumentData>> = { | ||
[Index in keyof Values]: ValueHookResult<Values[Index], FirestoreError>; | ||
} & { length: Values["length"] }; | ||
|
||
/** | ||
* Options to configure the subscription | ||
*/ | ||
export interface UseQueriesDataOptions { | ||
snapshotListenOptions?: SnapshotListenOptions; | ||
snapshotOptions?: SnapshotOptions; | ||
} | ||
|
||
/** | ||
* Returns and updates a the document data of multiple Firestore queries | ||
* | ||
* @template Values Tuple of types of the collection data | ||
* @param {Query[]} queries Firestore queries that will be subscribed to | ||
* @param {?UseQueriesDataOptions} options Options to configure the subscription | ||
* @returns {ValueHookResult[]} Array with tuple for each query: | ||
* * value: Query data; `undefined` if query is currently being fetched, or an error occurred | ||
* * loading: `true` while fetching the query; `false` if the query was fetched successfully or an error occurred | ||
* * error: `undefined` if no error occurred | ||
*/ | ||
export function useQueriesData<Values extends ReadonlyArray<DocumentData> = ReadonlyArray<DocumentData>>( | ||
queries: { [Index in keyof Values]: Query<Values[Index]> }, | ||
options?: UseQueriesDataOptions | ||
): UseQueriesDataResult<Values> { | ||
const { snapshotListenOptions = {}, snapshotOptions = {} } = options ?? {}; | ||
const onChange: UseMultiListenChange<Values[number], FirestoreError, Query<Values[number]>> = useCallback( | ||
(query, next, error) => | ||
onSnapshot(query, snapshotListenOptions, { | ||
next: (snap) => next(snap.docs.map((doc) => doc.data(snapshotOptions))), | ||
error, | ||
}), | ||
[] | ||
); | ||
|
||
// @ts-expect-error `useMultiListen` assumes a single value type | ||
return useMultiListen(queries, onChange, isQueryEqual); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.