Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Helper to use Firebase RTDB with React more conveniently. From https://pragli.com/blog/how-we-use-firebase-instead-of-redux-with-react
import React from 'react';
import firebase from 'firebase/app';
import equal from 'deep-equal';
function filterKeys(raw, allowed) {
if (!raw) {
return raw;
}
let s = new Set(allowed);
return Object.keys(raw)
.filter(key => s.has(key))
.reduce((obj, key) => {
obj[key] = raw[key];
return obj;
}, {});
}
function parseSpec(spec) {
let {template} = spec;
let starts = template.split('{')
starts.shift();
let propIds = [];
for (let start of starts) {
propIds.push(start.split('}')[0]);
}
let formatPath = (props) => {
let vals = props;
let path = template;
for (let key of Object.keys(vals)) {
if (!vals[key] && path.includes(`{${key}}`)) {
return null;
}
path = path.replace(`{${key}}`, vals[key]);
}
return path;
}
return { propIds, formatPath };
}
export function withDbData(specs) {
let propToSpecs = {};
for (let spec of specs) {
let {propIds} = parseSpec(spec);
for (let propId of propIds) {
if (!propToSpecs[propId]) {
propToSpecs[propId] = [];
}
propToSpecs[propId].push(spec);
}
}
return (Child) => {
let Wrapper = class extends React.PureComponent {
constructor(props) {
super(props);
this.unmounting = false;
this.offs = {};
this.state = {};
}
subscribeToSpec(spec) {
let { name, keys } = spec;
let { propIds, formatPath } = parseSpec(spec);
let path = formatPath(this.props);
if (!path) {
return;
}
let ref = firebase.database().ref(path);
let offFunc = ref.on('value', (snap) => {
let dat = keys ? filterKeys(snap.val(), keys) : snap.val();
if (equal(dat, this.state[name])) {
return;
}
this.setState({
[name]: dat,
});
});
let hasBeenOffed = false;
let off = () => {
if (hasBeenOffed) {
return;
}
hasBeenOffed = true;
if (!this.unmounting) {
this.setState({
[name]: null,
});
}
ref.off('value', offFunc);
};
for (let propId of propIds) {
if (!this.offs[propId]) {
this.offs[propId] = [];
}
this.offs[propId].push(off)
}
}
componentDidMount() {
for (let spec of specs) {
this.subscribeToSpec(spec)
}
}
componentDidUpdate(prevProps) {
let resubs = new Set();
for (let prop of Object.keys(propToSpecs)) {
if (prevProps[prop] !== this.props[prop]) {
if (this.offs[prop]) {
for (let off of this.offs[prop]) {
off();
}
}
this.offs[prop] = [];
for (let spec of propToSpecs[prop]) {
if (resubs.has(spec.name)) {
continue;
}
resubs.add(spec.name);
this.subscribeToSpec(spec);
}
}
}
}
componentWillUnmount() {
this.unmounting = true;
for (let offList of Object.values(this.offs)) {
for (let off of offList) {
off();
}
}
this.offs = {};
}
render() {
for (let spec of specs) {
if (spec.await && !this.state[spec.name]) {
return null;
}
}
let childProps = Object.assign({}, this.props, this.state);
return (<Child {... childProps} />);
}
}
return Wrapper;
}
}
@zomars

This comment has been minimized.

Copy link

zomars commented Dec 16, 2019

How about a React Hook version? Just for fun

@dsafreno

This comment has been minimized.

Copy link
Owner Author

dsafreno commented Jan 10, 2020

@zomars coming up soon 😃

@zomars

This comment has been minimized.

Copy link

zomars commented Jan 14, 2020

@kilroy05

This comment has been minimized.

Copy link

kilroy05 commented May 25, 2020

how about a firestore version?
would that be quite similar?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.