Last active
April 30, 2020 02:32
-
-
Save 1amageek/d51eb7257634be59840e321d2fe29045 to your computer and use it in GitHub Desktop.
Cloud Firestore Collection Reference hooks for React
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
import { useEffect, useState } from 'react' | |
import firebase, { database } from "firebase" | |
import "firebase/firestore" | |
import "firebase/auth" | |
import { firestore, Doc, DocumentReference } from '@1amageek/ballcap' | |
export const useDocumentListen = <T extends Doc>(type: typeof Doc, documentReference?: DocumentReference, waiting: boolean = false): [T | undefined, boolean, Error?] => { | |
interface Prop { | |
data?: T | |
loading: boolean | |
error?: Error | |
} | |
const [state, setState] = useState<Prop>({ loading: true }) | |
useEffect(() => { | |
let enabled = true | |
let listener: (() => void) | undefined | |
const listen = async (documentReference: DocumentReference) => { | |
listener = documentReference.onSnapshot({ | |
next: (snapshot) => { | |
if (snapshot.exists) { | |
const data = type.fromSnapshot<T>(snapshot) | |
if (enabled) { | |
setState({ | |
data: data, | |
loading: false, | |
error: undefined | |
}) | |
} | |
} else { | |
if (enabled) { | |
setState({ | |
data: undefined, | |
loading: false, | |
error: undefined | |
}) | |
} | |
} | |
}, | |
error: (error) => { | |
if (enabled) { | |
setState({ | |
data: undefined, | |
loading: false, | |
error | |
}) | |
} | |
} | |
}) | |
} | |
if (!waiting) { | |
if (documentReference) { | |
if (enabled) { | |
if (!state.loading) { | |
setState({ | |
data: undefined, | |
loading: true, | |
error: undefined | |
}) | |
} | |
} | |
listen(documentReference) | |
} else { | |
if (enabled) { | |
setState({ | |
data: undefined, | |
loading: false, | |
error: undefined | |
}) | |
} | |
} | |
} else { | |
if (enabled) { | |
setState({ | |
data: undefined, | |
loading: waiting, | |
error: undefined | |
}) | |
} | |
} | |
return () => { | |
enabled = false | |
if (listener) { | |
listener() | |
} | |
} | |
}, [documentReference?.path, waiting]) | |
return [state.data, state.loading, state.error] | |
} | |
export interface WhereQuery { | |
fieldPath: string | firebase.firestore.FieldPath | |
opStr: firebase.firestore.WhereFilterOp | |
value: any | |
} | |
export interface OrderByQuery { | |
fieldPath: string | firebase.firestore.FieldPath, | |
directionStr?: firebase.firestore.OrderByDirection | |
} | |
export const Where = (fieldPath: string | firebase.firestore.FieldPath, | |
opStr: firebase.firestore.WhereFilterOp, | |
value: any): WhereQuery => { | |
return { fieldPath, opStr, value } | |
} | |
export const OrderBy = (fieldPath: string | firebase.firestore.FieldPath, | |
directionStr?: firebase.firestore.OrderByDirection): OrderByQuery => { | |
return { fieldPath, directionStr } | |
} | |
export interface Query { | |
path?: string | |
wheres?: Array<WhereQuery | undefined> | |
orderBy?: OrderByQuery | |
limit?: number | |
} | |
export const useDataSourceListen = <T extends Doc>(type: typeof Doc, query?: Query, waiting: boolean = false): [T[], boolean, Error | undefined] => { | |
interface Prop { | |
data: T[] | |
loading: boolean | |
error?: Error | |
} | |
const [state, setState] = useState<Prop>({ data: [], loading: true }) | |
let ref: firebase.firestore.Query | undefined = (query && query.path) ? firestore.collection(query.path) : undefined | |
if (query?.wheres) { | |
query.wheres.forEach(where => { | |
if (where) { | |
const { fieldPath, opStr, value } = where | |
ref = ref?.where(fieldPath, opStr, value) | |
} | |
}) | |
} | |
if (query?.orderBy) { | |
const { fieldPath, directionStr } = query.orderBy | |
ref = ref?.orderBy(fieldPath, directionStr) | |
} | |
if (query?.limit) { | |
ref = ref?.limit(query.limit) | |
} | |
useEffect(() => { | |
let enabled = true | |
let listener: (() => void) | undefined | |
const listen = async () => { | |
listener = ref?.onSnapshot({ | |
next: (snapshot) => { | |
const data = snapshot.docs.map(doc => type.fromSnapshot<T>(doc)) | |
if (enabled) { | |
setState({ | |
data, | |
loading: false, | |
error: undefined | |
}); | |
} | |
}, | |
error: (error) => { | |
console.error(error) | |
if (enabled) { | |
setState({ | |
data: [], | |
loading: false, | |
error | |
}) | |
} | |
} | |
}) | |
}; | |
if (!waiting) { | |
if (ref) { | |
listen() | |
} else { | |
setState({ | |
data: [], | |
loading: false, | |
error: undefined | |
}) | |
} | |
} else { | |
setState({ | |
data: [], | |
loading: waiting, | |
error: undefined | |
}) | |
} | |
return () => { | |
enabled = false | |
if (listener) { | |
listener() | |
} | |
} | |
}, [query?.path, JSON.stringify(query?.wheres), JSON.stringify(query?.orderBy), query?.limit, waiting]) | |
return [state.data, state.loading, state.error] | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment