Created
March 10, 2019 20:56
-
-
Save zwily/40f32f7b480e2bad9249b72a1e5b2e85 to your computer and use it in GitHub Desktop.
Firebase throttled updater hook
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 { useState, useRef } from "react"; | |
import { useDocument } from "react-firebase-hooks/firestore"; | |
import throttle from "lodash/throttle"; | |
// Hook that gives you an updater function that will do throttled | |
// updates to a firebase document, and provide back to you views of | |
// the updated data (before it has persisted). | |
// | |
// arguments: a Document ref, and time in ms to throttle | |
// returns an object with following fields: | |
// error: any error from reading the document | |
// (from react-firebase-hooks/firestore useDocument) | |
// loading: true when document is loading | |
// saving: is not-null (an object) when document is saving. | |
// (Can be used for a "Saving..." indicator) | |
// snapshot: the Firebase snapshot of the document. | |
// This will not include any pending changes! | |
// data: an object representing the current Firebase document, | |
// and any pending and saving changes applied. | |
// (this is usually what you'd show in the UI) | |
// updater: a function(updates, delayed). updates should be an object | |
// representing changes to make in the document, delayed | |
// is a boolean. If it is false, changes are saved | |
// immediately. Otherwise, throttled according to the | |
// configured throttleMs | |
export default function useFirebaseUpdater(ref, throttleMs = 3000) { | |
const [dirty, setDirty] = useState({}); | |
const [saving, setSaving] = useState(null); | |
const { error, loading, value: snapshot } = useDocument(ref); | |
const updateNow = () => { | |
setDirty(dirtyData => { | |
setSaving(dirtyData); | |
// TODO: remove any keys with identical values to what we already have. | |
if (Object.keys(dirtyData).length > 0) { | |
ref.set(dirtyData, { merge: true }).then(() => { | |
setSaving(null); | |
}); | |
} | |
return {}; | |
}); | |
}; | |
const throttledRef = useRef( | |
throttle(updateNow, throttleMs, { | |
leading: false, | |
trailing: true | |
}) | |
); | |
function updater(updates, delayed) { | |
setDirty(Object.assign({}, dirty, updates)); | |
if (!delayed) { | |
throttledRef.current.cancel(); | |
updateNow(); | |
} else { | |
throttledRef.current(); | |
} | |
} | |
return { | |
error, | |
loading, | |
saving, | |
snapshot, | |
data: Object.assign( | |
{}, | |
snapshot ? snapshot.data() : {}, | |
saving || {}, | |
dirty || {} | |
), | |
updater | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment