Skip to content

Instantly share code, notes, and snippets.

@marcello3d
Created September 12, 2016 00:29
Show Gist options
  • Save marcello3d/0e9a1e532954f67c1fbb67c7d18319a6 to your computer and use it in GitHub Desktop.
Save marcello3d/0e9a1e532954f67c1fbb67c7d18319a6 to your computer and use it in GitHub Desktop.
Experimental ES6 Firebase, Observables, and React
import React from 'react'
export default function connect (propFunctions) {
return function wrapWithConnect (WrappedComponent) {
return class Connect extends React.Component {
static displayName = `Observing${WrappedComponent.displayName}`
componentWillMount () {
this._subscriptions = Object.keys(propFunctions).map((key) =>
this._subscribe(key, propFunctions[key](this.props)))
}
_subscribe (key, result) {
const onValue = (value) => {
if (this._subscriptions) {
this.setState({ [key]: value })
}
}
const onError = (error) => {
if (this._subscriptions) {
this.setState({ [`${key}__error`]: error })
}
}
// check for Observable
if (typeof result === 'object' && typeof result.subscribe === 'function') {
return result.subscribe(onValue, onError)
}
if (result instanceof Promise) {
result.then(onValue).catch(onError)
} else {
onValue(result)
}
return { unsubscribe () {} }
}
componentWillUnmount () {
this._subscriptions.forEach((subscription) => subscription.unsubscribe())
this._subscriptions = null
}
render () {
return (
<WrappedComponent
{...this.state}
{...this.props}
/>
)
}
}
}
}
import React from 'react'
import { observeValue, observeCurrentUser } from './firebase-observable'
import connectObserver from './connect-observer'
export default connectObserver({
post: ({ postId }) => observeValue(firebase.database().ref('/posts').child(postId)),
user: () => observeCurrentUser(firebase.auth())
})(class Post extends React.Component
static propTypes = {
postId: React.PropTypes.string.isRequired,
post: React.PropTypes.shape({ … }),
user: React.PropTypes.object
};
render () {
const { post, user } = this.props
return …
}
}))
import Observable from 'zen-observable'
function observeSnapshot (reference, mapper) {
return new Observable((observer) => {
const onValue = (snapshot) => observer.next(mapper(snapshot))
const onError = (error) => observer.error(error)
reference.on('value', onValue, onError)
return () => reference.off('value', onValue)
})
}
export function observeValue (reference) {
return observeSnapshot(reference, (snapshot) => snapshot.val())
}
export function observeKeys (reference) {
return observeSnapshot(reference, (snapshot) => {
const keys = []
snapshot.forEach((childSnapshot) => {
keys.push(childSnapshot.key)
})
return keys
})
}
export function observeKeyCount (reference) {
return observeSnapshot(reference, (snapshot) => snapshot.numChildren())
}
export function observeArrayValue (reference) {
return observeSnapshot(reference, (snapshot) => {
const array = []
snapshot.forEach((childSnapshot) => {
const object = {
'.key': childSnapshot.key,
'.priority': childSnapshot.getPriority()
}
const value = childSnapshot.val()
if (typeof value === 'object') {
Object.assign(object, value)
} else {
object['.value'] = value
}
array.push(object)
})
return array
})
}
export function observeCurrentUser (firebaseAuth) {
return new Observable((observer) => {
const onValue = (user) => observer.next(user)
const unsubscribe = firebaseAuth.onAuthStateChanged(onValue)
onValue(firebaseAuth.currentUser)
return unsubscribe
})
}
@marcello3d
Copy link
Author

TODO: componentWillReceiveProps support

@jonatansberg
Copy link

@marcello3d What would be a good way to handle "actions" (like a set or transaction) along side this? Something comparable to react-redux's mapDispatchToProps would be nice..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment