Skip to content

Instantly share code, notes, and snippets.

@zwily
Created March 10, 2019 20:56
Show Gist options
  • Save zwily/40f32f7b480e2bad9249b72a1e5b2e85 to your computer and use it in GitHub Desktop.
Save zwily/40f32f7b480e2bad9249b72a1e5b2e85 to your computer and use it in GitHub Desktop.
Firebase throttled updater hook
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