-
-
Save brense/60b1ad9874b0ee9c6d9a77d6568de99e to your computer and use it in GitHub Desktop.
some hooks wrapping firebase client lib
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 React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' | |
import Firebase from 'firebase/app' | |
import 'firebase/firestore' | |
const FirebaseContext = React.createContext<{ app: Firebase.app.App }>({ app: {} as unknown as Firebase.app.App }) | |
export function FirebaseProvider({ children, app }: React.PropsWithChildren<{ app: Firebase.app.App }>) { | |
return <FirebaseContext.Provider value={{ app }}>{children}</FirebaseContext.Provider> | |
} | |
export function useFirebase() { | |
const { app } = useContext(FirebaseContext) | |
return app | |
} | |
export function useFirestore() { | |
const { app } = useContext(FirebaseContext) | |
return useMemo(() => app.firestore(), [app]) | |
} | |
export function useStorage() { | |
const { app } = useContext(FirebaseContext) | |
return useMemo(() => app.storage(), [app]) | |
} | |
export function useAuth() { | |
const { app } = useContext(FirebaseContext) | |
return useMemo(() => app.auth(), [app]) | |
} | |
export function useCollection<T extends {}>(collectionPath: string, queryCallback?: (collection: Firebase.firestore.CollectionReference) => Firebase.firestore.Query | Firebase.firestore.CollectionReference) { | |
const [{ docs, loading }, setState] = useState<{ loading: boolean, docs: Array<T & { id: string }> }>({ loading: false, docs: [] }) | |
const firestore = useFirestore() | |
const collection = useMemo(() => firestore.collection(collectionPath), [firestore, collectionPath]) | |
useEffect(() => { | |
setState({ loading: true, docs: [] }) | |
const snapshot = queryCallback ? queryCallback(collection).get() : collection.get() | |
snapshot.then(s => { | |
setState(() => ({ loading: false, docs: s.docs.map(doc => ({ id: doc.id, ...doc.data() as T })) })) | |
}) | |
}, [collection, queryCallback]) | |
const handleAdd = useCallback(async (data: T, disableStateUpdate: boolean = false) => { | |
const ref = await collection.add(data) | |
if (!disableStateUpdate) { | |
setState(d => ({ loading: d.loading, docs: [{ id: ref.id, ...data }, ...d.docs] })) | |
} | |
return ref | |
}, [collection]) | |
const handleSet = useCallback(async (docId: string, data: T, options?: { batch?: Firebase.firestore.WriteBatch, merge?: boolean }) => { | |
options?.batch ? options.batch.set(collection.doc(docId), data, { merge: options?.merge || false }) : await collection.doc(docId).set(data, { merge: options?.merge || false }) | |
if (!options || !options.batch) { | |
setState(d => { | |
const index = d.docs.findIndex(doc => doc.id === docId) | |
d.docs[index] = { id: docId, ...data } | |
return { loading: d.loading, docs: [...d.docs] } | |
}) | |
} | |
}, [collection]) | |
const handleUpdate = useCallback(async (docId: string, data: Partial<T>, options?: { batch?: Firebase.firestore.WriteBatch }) => { | |
options?.batch ? options.batch.update(collection.doc(docId), data) : await collection.doc(docId).update(data) | |
if (!options || !options.batch) { | |
setState(d => { | |
const index = d.docs.findIndex(doc => doc.id === docId) | |
d.docs[index] = { ...d.docs[index], ...data } | |
return { loading: d.loading, docs: [...d.docs] } | |
}) | |
} | |
}, [collection]) | |
const handleDelete = useCallback(async (docId: string, options?: { batch?: Firebase.firestore.WriteBatch }) => { | |
options?.batch ? options.batch.delete(collection.doc(docId)) : await collection.doc(docId).delete() | |
if (!options || !options.batch) { | |
setState(d => { | |
const index = d.docs.findIndex(doc => doc.id === docId) | |
index >= 0 && d.docs.splice(index, 1) | |
return { loading: d.loading, docs: [...d.docs] } | |
}) | |
} | |
}, [collection]) | |
const setDocs = useCallback((docs: Array<T & { id: string }>) => { | |
setState(s => ({ ...s, docs: [...docs] })) | |
}, []) | |
return [docs, setDocs, { loading, add: handleAdd, set: handleSet, update: handleUpdate, delete: handleDelete }] as [typeof docs, typeof setDocs, { loading: boolean, add: typeof handleAdd, set: typeof handleSet, update: typeof handleUpdate, delete: typeof handleDelete }] | |
} | |
export function useDoc<T extends {}>(collectionPath: string, docId: string) { | |
const [{ loading, doc }, setState] = useState({ loading: false, doc: null as null | T }) | |
const firestore = useFirestore() | |
const docRef = useMemo(() => docId !== '' ? firestore.collection(collectionPath).doc(docId) : null, [firestore, collectionPath, docId]) | |
useEffect(() => { | |
setState({ loading: true, doc: null }) | |
docRef?.get().then(doc => { | |
setState({ loading: false, doc: { id: doc.id, ...doc.data() as T } }) | |
}) | |
}, [docRef]) | |
const handleSet = useCallback(async (data: T, options?: { merge?: boolean }) => { | |
console.log(data) | |
await docRef?.set(data as any, { merge: options?.merge || false }) | |
setState(s => ({ loading: s.loading, doc: { id: docRef?.id, ...data } })) | |
}, [docRef]) | |
const handleUpdate = useCallback(async (data: Partial<T>) => { | |
await docRef?.update(data) | |
setState(s => ({ loading: s.loading, doc: { ...s.doc, ...data } as T })) | |
}, [docRef]) | |
return [doc, { loading, set: handleSet, update: handleUpdate }] as [typeof doc, { loading: boolean, set: typeof handleSet, update: typeof handleUpdate }] | |
} | |
export function useFirebaseAuthUser(authStateChangedCallback?: (user: Firebase.User | null) => void) { | |
const [user, setUser] = useState<Firebase.User | null>(null) | |
const auth = useAuth() | |
useEffect(() => { | |
auth.onAuthStateChanged(user => { | |
setUser(user) | |
authStateChangedCallback && authStateChangedCallback(user) | |
}) | |
}, [auth, authStateChangedCallback]) | |
return user | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment