react-firestore
Version:
React components to fetch data from firestore using render props
353 lines (285 loc) • 9.32 kB
JavaScript
import React, { Component } from 'preact';
import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose';
import _extends from '@babel/runtime/helpers/esm/extends';
import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose';
import hoistStatics from 'hoist-non-react-statics';
var FirestoreCache = function () {};
var FirestoreContext = React.createContext(null);
var FirestoreProvider =
/*#__PURE__*/
function (_Component) {
_inheritsLoose(FirestoreProvider, _Component);
function FirestoreProvider(props) {
var _this = _Component.call(this, props) || this;
var firebase = props.firebase,
useTimestampsInSnapshots = props.useTimestampsInSnapshots;
var firestore = firebase.firestore();
if (typeof useTimestampsInSnapshots !== 'undefined') {
firestore.settings({
timestampsInSnapshots: useTimestampsInSnapshots
});
}
_this.state = {
firestoreDatabase: firestore,
firestoreCache: new FirestoreCache()
};
return _this;
}
var _proto = FirestoreProvider.prototype;
_proto.render = function render() {
return React.h(FirestoreContext.Provider, {
value: this.state
}, this.props.children);
};
return FirestoreProvider;
}(Component);
FirestoreProvider.defaultProps = {};
var Firestore = function (_ref) {
var render = _ref.render;
return React.h(FirestoreContext.Consumer, null, function (_ref2) {
var firestoreDatabase = _ref2.firestoreDatabase;
return render({
firestore: firestoreDatabase
});
});
};
var withFirestore = function (Component) {
var C = function (_ref) {
var wrappedComponentRef = _ref.wrappedComponentRef,
remainingProps = _objectWithoutPropertiesLoose(_ref, ["wrappedComponentRef"]);
return React.h(FirestoreContext.Consumer, null, function (value) {
if (!value) {
throw new Error('FirestoreProvider is missing');
}
var firestoreDatabase = value.firestoreDatabase;
return React.h(Component, _extends({}, remainingProps, {
firestore: firestoreDatabase,
ref: wrappedComponentRef
}));
});
};
C.displayName = "withFirestore(" + (Component.displayName || Component.name) + ")";
C.WrappedComponent = Component;
return hoistStatics(C, Component);
};
/**
* Deep equality comparison for Arrays
* @param {Array} a The array to compare against
* @param {Array} b The array to compare with
* @returns {boolean} If the two arrays are equal
*/
function deepEqual(a, b) {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!deepEqual(a[i], b[i])) {
return false;
}
}
return true;
} else {
return a === b;
}
}
var FirestoreCollection =
/*#__PURE__*/
function (_Component) {
_inheritsLoose(FirestoreCollection, _Component);
function FirestoreCollection() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _Component.call.apply(_Component, [this].concat(args)) || this;
_this.state = {
isLoading: true,
data: [],
error: null,
snapshot: null
};
_this.setupFirestoreListener = function () {
var _this$props = _this.props,
firestore = _this$props.firestore,
path = _this$props.path,
queryProps = _objectWithoutPropertiesLoose(_this$props, ["firestore", "path"]);
var collectionRef = firestore.collection(path);
var query = _this.buildQuery(collectionRef, queryProps);
_this.unsubscribe = query.onSnapshot(_this.handleOnSnapshotSuccess, _this.handleOnSnapshotError);
};
_this.handleOnSnapshotSuccess = function (snapshot) {
if (snapshot) {
_this.setState({
isLoading: false,
data: snapshot.docs.map(function (doc) {
return _extends({
id: doc.id
}, doc.data());
}),
error: null,
snapshot: snapshot
});
}
};
_this.handleOnSnapshotError = function (error) {
_this.setState({
isLoading: false,
data: [],
error: error,
snapshot: null
});
};
_this.buildQuery = function (collectionRef, queryProps) {
var sort = queryProps.sort,
limit = queryProps.limit,
filter = queryProps.filter;
var query = collectionRef;
if (sort) {
sort.split(',').forEach(function (sortItem) {
var _sortItem$split = sortItem.split(':'),
field = _sortItem$split[0],
order = _sortItem$split[1];
query = query.orderBy(field, order);
});
}
if (limit) {
query = query.limit(limit);
}
if (filter) {
//if filter is array of array, build the compound query
if (Array.isArray(filter[0])) {
filter.forEach(function (clause) {
var _query;
query = (_query = query).where.apply(_query, clause);
});
} else {
var _query2;
//build the simple query
query = (_query2 = query).where.apply(_query2, filter);
}
}
return query;
};
return _this;
}
var _proto = FirestoreCollection.prototype;
_proto.componentDidMount = function componentDidMount() {
this.setupFirestoreListener();
};
_proto.componentWillUnmount = function componentWillUnmount() {
this.handleUnsubscribe();
};
_proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
var _this2 = this;
if (nextProps.path !== this.props.path || nextProps.sort !== this.props.sort || nextProps.limit !== this.props.limit || !deepEqual(nextProps.filter, this.props.filter)) {
this.handleUnsubscribe();
this.setState({
isLoading: true
}, function () {
return _this2.setupFirestoreListener();
});
}
};
_proto.handleUnsubscribe = function handleUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe();
}
};
_proto.render = function () {
var _this$props2 = this.props,
children = _this$props2.children,
render = _this$props2.render;
if (render) return render(this.state);
if (typeof children === 'function') return children(this.state);
return null;
};
return FirestoreCollection;
}(Component);
var FirestoreCollection$1 = withFirestore(FirestoreCollection);
var FirestoreDocument =
/*#__PURE__*/
function (_Component) {
_inheritsLoose(FirestoreDocument, _Component);
function FirestoreDocument() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _Component.call.apply(_Component, [this].concat(args)) || this;
_this.state = {
isLoading: true,
data: null,
error: null,
snapshot: null
};
_this.setupFirestoreListener = function () {
var _this$props = _this.props,
firestore = _this$props.firestore,
path = _this$props.path;
var documentRef = firestore.doc(path);
_this.unsubscribe = documentRef.onSnapshot(_this.handleOnSnapshotSuccess, _this.handleOnSnapshotError);
};
_this.handleOnSnapshotError = function (error) {
_this.setState({
isLoading: false,
error: error,
data: null,
snapshot: null
});
};
_this.handleOnSnapshotSuccess = function (snapshot) {
if (snapshot) {
var newState = {
isLoading: false,
error: null,
snapshot: snapshot
};
try {
var documentData = snapshot.data();
newState.data = _extends({
id: snapshot.id
}, documentData);
} catch (error) {
newState.error = error;
}
_this.setState(newState);
}
};
return _this;
}
var _proto = FirestoreDocument.prototype;
_proto.componentDidMount = function componentDidMount() {
this.setupFirestoreListener();
};
_proto.componentWillUnmount = function componentWillUnmount() {
this.handleUnsubscribe();
};
_proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
var _this2 = this;
if (nextProps.path !== this.props.path) {
this.handleUnsubscribe();
this.setState({
isLoading: true
}, function () {
return _this2.setupFirestoreListener();
});
}
};
_proto.handleUnsubscribe = function handleUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe();
}
};
_proto.render = function () {
var _this$props2 = this.props,
children = _this$props2.children,
render = _this$props2.render;
if (render) return render(this.state);
if (typeof children === 'function') return children(this.state);
return null;
};
return FirestoreDocument;
}(Component);
var FirestoreDocument$1 = withFirestore(FirestoreDocument);
export { Firestore, FirestoreProvider, FirestoreCollection$1 as FirestoreCollection, FirestoreDocument$1 as FirestoreDocument, withFirestore };