Skip to content

Instantly share code, notes, and snippets.

@josephcc
Created February 28, 2019 08:24
Show Gist options
  • Save josephcc/1d09e982ac5ca120812673c3b8121665 to your computer and use it in GitHub Desktop.
Save josephcc/1d09e982ac5ca120812673c3b8121665 to your computer and use it in GitHub Desktop.
import React, { useState, useRef, useContext } from 'react'
import { FirebaseContext } from 'bentowidgets/widgets/firebase_context'
import firebase from 'firebase'
const FirestoreCacheContext = React.createContext()
function FirestoreCacheProvider(props) {
const _firebase = useContext(FirebaseContext)
let cache = useRef({}).current // HACKY AF
let recency = useRef([]).current // HACKY AF
let [limit, setCacheLimit] = useState(100)
const { children } = props
const onSnapshotQuery = firebase.firestore.Query.prototype.onSnapshot
const onSnapshotDocument = firebase.firestore.DocumentReference.prototype.onSnapshot
function updateCache(key, snapshot) {
if (key in cache) {
const index = recency.indexOf(key)
recency.splice(index, 1)
recency.push(key)
//console.log(`CACHE update: ${key}`)
} else {
recency.push(key)
//console.log(`CACHE insert: ${key}`)
while (recency.length > limit) {
const removeKey = recency.shift()
delete cache[removeKey]
//console.log(`CACHE remove: ${removeKey}`)
}
}
cache[key] = snapshot
//console.log(`CACHE size: ${recency.length}`)
//console.log(recency)
}
function keyForPath(path) {
return `${path.segments}_${path.len}_${path.length}_${path.offset}`
}
function keyForFilter(filter) {
return `${keyForPath(filter.field)}:${filter.op}:${filter.value}`
}
function keyForOrderBy(orderBy) {
return `${keyForPath(orderBy.field)}:${orderBy.dir}:${orderBy.isKeyOrderBy}`
}
function keyForQuery(query) {
let key = `${query.endAt}:::${query.explicitOrderBy}:::${query.limit}`
key = `${key}:::${query.memoizedCanonicalId}:::${query.memoizedOrderBy}`
key = `${key}:::${query.startAt}:::`
key = `${key}:::${(query.filters || []).map(keyForFilter)}`
key = `${key}:::${(query.orderBy || []).map(keyForOrderBy)}`
key = `${key}:::${query.path && keyForPath(query.path)}`
return key
}
firebase.firestore.Query.prototype.onSnapshot = function(arg1, arg2, arg3) {
const key = keyForQuery(this._query) // eslint-disable-line react/no-this-in-sfc
if (key in cache) {
arg1(cache[key])
}
const _arg1 = snapshot => {
updateCache(key, snapshot)
return arg1(snapshot)
}
return onSnapshotQuery.call(this, _arg1, arg2, arg3)
}
firebase.firestore.DocumentReference.prototype.onSnapshot = function(arg1, arg2, arg3) {
const key = `DOC:${this.path}` // eslint-disable-line react/no-this-in-sfc
if (key in cache) {
arg1(cache[key])
}
const _arg1 = snapshot => {
updateCache(key, snapshot)
return arg1(snapshot)
}
return onSnapshotDocument.call(this, _arg1, arg2, arg3)
}
return (
<FirestoreCacheContext.Provider value={{ firebase: _firebase, setCacheLimit: setCacheLimit }}>
{children}
</FirestoreCacheContext.Provider>
)
}
function withFirestoreCache(Component) {
return function FirestoreCacheComponent(props) {
return (
<FirestoreCacheContext.Consumer>
{context => <Component {...props} firebase={context.firebase} setCacheLimit={context.setCacheLimit} />}
</FirestoreCacheContext.Consumer>
)
}
}
export default FirestoreCacheProvider
export { FirestoreCacheContext, withFirestoreCache, FirestoreCacheProvider }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment