Last active
November 4, 2017 00:07
-
-
Save adampash/61d8c8308d8852219e35f04a17acc7d4 to your computer and use it in GitHub Desktop.
HoC for connecting React components to Firestore
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 React, { Component } from 'react'; | |
// I used redux's compose b/c it was there, but you could of course compose however you wanted | |
import { compose } from 'redux'; | |
// db is just the return of firebase.firestore() from an initialized Firebase | |
import { db } from './store'; | |
// pipe and reduce are from https://github.com/adampash/pipe/blob/master/src/index.js | |
import { pipe, reduce } from './utils/pipe'; | |
class FirestoreData extends Component { | |
constructor(props) { | |
super(props); | |
this.state = {}; | |
} | |
componentWillMount() { | |
const { path, propKey } = this.props; | |
console.log(`path`, path); | |
if (!path) return; | |
const splitPath = path.split('/'); | |
const finalPropKey = propKey || splitPath.slice(-1)[0]; | |
this.setState({ [finalPropKey]: null }); | |
if (splitPath.length % 2 === 0) | |
return this.handleCollection(path, finalPropKey); | |
return this.handleDoc(path, finalPropKey); | |
} | |
componentWillReceiveProps({ path, propKey }) { | |
if (!path) return; | |
const splitPath = path.split('/'); | |
const finalPropKey = propKey || splitPath.slice(-1)[0]; | |
this.setState({ [finalPropKey]: null }); | |
if (splitPath.length % 2 === 0) | |
return this.handleCollection(path, finalPropKey); | |
return this.handleDoc(path, finalPropKey); | |
} | |
componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
handleWhere(accRef, query) { | |
return Array.isArray(query) | |
? accRef.where(...query) | |
: accRef.where(...query.split(/\s+/)); | |
} | |
handleCollection(path, propKey) { | |
const { where, orderBy, startAt, startAfter, limit, refOnly } = this.props; | |
const initialRef = db.collection(path); | |
if (refOnly) { | |
this.setState({ | |
collectionRef: initialRef, | |
}); | |
return; | |
} | |
const finalRef = pipe(where)( | |
reduce(this.handleWhere, initialRef), | |
ref => (orderBy !== undefined ? ref.orderBy(...orderBy) : ref), | |
ref => (startAt !== undefined ? ref.startAt(startAt) : ref), | |
ref => (startAfter !== undefined ? ref.startAfter(startAfter) : ref), | |
ref => (limit !== undefined ? ref.limit(limit) : ref) | |
); | |
this.unsubscribe = finalRef.onSnapshot(collection => | |
this.setState({ | |
collectionRef: initialRef, | |
[propKey]: collection.docs.map(doc => ({ | |
uid: doc.id, | |
...doc.data(), | |
docRef: doc.ref, | |
})), | |
}) | |
); | |
} | |
handleDoc(path, propKey) { | |
const ref = db.doc(path); | |
this.unsubscribe = ref.onSnapshot(doc => { | |
if (doc.exists) this.setState({ [propKey]: { uid: doc.id, ...doc.data(), docRef: doc.ref }); | |
}); | |
} | |
render() { | |
const { | |
Component, | |
path, | |
propKey, | |
where, | |
limit, | |
orderBy, | |
startAt, | |
startAfter, | |
...props | |
} = this.props; | |
return <Component {...this.state} {...props} />; | |
} | |
} | |
const firestoreConnect = ({ | |
path, | |
propKey, | |
where = [], | |
limit, | |
orderBy, | |
startAt, | |
startAfter, | |
}) => Component => props => ( | |
<FirestoreData | |
Component={Component} | |
path={path instanceof Function ? path(props) : path} | |
propKey={propKey} | |
where={where} | |
limit={limit} | |
orderBy={orderBy} | |
startAt={startAt} | |
startAfter={startAfter} | |
{...props} | |
/> | |
); | |
const multiConnect = connections => Component => props => { | |
if (Array.isArray(connections)) { | |
const composed = compose( | |
...connections.map(connection => firestoreConnect(connection)) | |
); | |
return composed(Component)(props); | |
} | |
return firestoreConnect(connections)(Component)(props); | |
}; | |
export default multiConnect; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I sort of stole a lot of the connect API from
react-redux-firebase
but it didn't have Firestore support yet. Plus I didn't really want it in redux/understand the point of putting it in redux.Usage for a collection:
Usage for a document:
If you want to connect to multiple pieces of data, you could pass an array like:
You also get a
docRef
to do things likedocRef.update(data)
orcollectionRef
to do things likecollectionRef.add(data)
, depending on what you asked for. I haven't thought all the edges through entirely, it's a work in progress.