A Relay inspired library for building React.js + Firebase applications.
Questions? Find me on twitter at @tylermcginnis
React.js makes managing state easy to reason about. Firebase makes persisting your data easy to implement. re-base, inspired by Relay, combines the benefits of React and Firebase by allowing each component to specify its own data dependency. Forget about your data persistence and focus on what really matters, your application's state.
I spent a few weeks trying to figure out the cleanest way to implement Firebase into my React/Flux application. After struggling for a bit, I tweeted my frustrations. I was enlightened to the fact that Firebase and Flux really don't work well together. It makes sense why they don't work together, because they're both trying to accomplish roughly the same thing. So I did away with my reliance upon Flux and tried to think of a clean way to implement React with Firebase. I came across ReactFire built by Jacob Wenger at Firebase and loved his idea. Sync a Firebase endpoint with a property on your component's state. So whenever your data changes, your state will be updated. Simple as that. The problem with ReactFire is because it uses Mixins, it's not compatible with ES6 classes. After chatting with Jacob Turner, we wanted to create a way to allow the one way binding of ReactFire with ES6 classes along some more features like two way data binding and listening to Firebase endpoints without actually binding a state property to them. Thus, re-base was built.
$ npm install --save re-base firebase
For more in depth examples, see the examples
folder.
Accepts an initialized firebase database object
An instance of re-base.
var Rebase = require('re-base');
var firebase = require('firebase');
var app = firebase.initializeApp({
apiKey: 'apiKey',
authDomain: 'projectId.firebaseapp.com',
databaseURL: 'https://databaseName.firebaseio.com',
storageBucket: 'bucket.appspot.com',
messagingSenderId: 'xxxxxxxxxxxxxx'
});
var base = Rebase.createClass(app.database());
var Rebase = require('re-base');
var firebase = require('firebase/app');
var database = require('firebase/database');
var app = firebase.initializeApp({
apiKey: 'apiKey',
authDomain: 'projectId.firebaseapp.com',
databaseURL: 'https://databaseName.firebaseio.com',
storageBucket: 'bucket.appspot.com',
messagingSenderId: 'xxxxxxxxxxxxxx'
});
var db = firebase.database(app);
var base = Rebase.createClass(db);
This property contains the initialized firebase app that was passed into re-base. You can access any of the firebase services that you are using off this object. For instance, if you want to use some firebase database
methods that re-base doesn't have helpers for or if you are using the auth
service and want to quickly access it off of re-base.
This property contains an object that you can use when adding data that will be converted to a timestamp by Firebase. See the docs for more info.
Allows you to set up two way data binding between your component's state and your Firebase. Whenever your Firebase changes, your component's state will change. Whenever your component's state changes, Firebase will change.
foo.bar
this.state.loading
to false.An object which you can pass to removeBinding
if you want to remove the listener while the component is still mounted.
componentDidMount(){
base.syncState(`shoppingList`, {
context: this,
state: 'items',
asArray: true
});
}
addItem(newItem){
this.setState({
items: this.state.items.concat([newItem]) //updates Firebase and the local state
});
}
One way data binding from Firebase to your component's state. Allows you to bind a component's state property to a Firebase endpoint so whenever that Firebase endpoint changes, your component's state will be updated with that change.
foo.bar
(no arrays)this.state.loading
to false.An object which you can pass to removeBinding
if you want to remove the listener while the component is still mounted.
componentDidMount(){
base.bindToState('tasks', {
context: this,
state: 'tasks',
asArray: true
});
}
Allows you to listen to Firebase endpoints without binding those changes to a state property. Instead, a callback will be invoked with the newly updated data.
An object which you can pass to removeBinding
when your component unmounts to remove the Firebase listeners.
componentDidMount(){
base.listenTo('votes', {
context: this,
asArray: true,
then(votesData){
var total = 0;
votesData.forEach((vote, index) => {
total += vote
});
this.setState({total});
}
})
}
Allows you to retrieve the data from a Firebase endpoint just once without subscribing or listening for data changes.
A Firebase Promise which resolves when the write is complete and rejects if there is an error
Using callback
getSales(){
base.fetch('sales', {
context: this,
asArray: true,
then(data){
console.log(data);
}
});
}
Using Promise
getSales(){
base.fetch('sales', {
context: this,
asArray: true
}).then(data => {
console.log(data);
}).catch(error => {
//handle error
})
}
Allows you to update a Firebase endpoint with new data. Replace all the data at this endpoint with the new data
A Firebase Promise which resolves when the write is complete and rejects if there is an error
Using callback
addUser(){
base.post(`users/${userId}`, {
data: {name: 'Tyler McGinnis', age: 25},
then(err){
if(!err){
Router.transitionTo('dashboard');
}
}
});
}
Using promise
addUser(){
base.post(`users/${userId}`, {
data: {name: 'Tyler McGinnis', age: 25}
}).then(() => {
Router.transitionTo('dashboard');
}).catch(err => {
// handle error
});
}
Allows you to add data to a Firebase endpoint. Adds data to a child of the endpoint with a new Firebase push key
A Firebase ThenableReference which is defined by Firebase as a "Combined Promise and reference; resolves when write is complete, but can be used immediately as the reference to the child location."
Using callback
//
addBear(){
var immediatelyAvailableReference = base.push('bears', {
data: {name: 'George', type: 'Grizzly'},
then(err){
if(!err){
Router.transitionTo('dashboard');
}
}
});
//available immediately, you don't have to wait for the callback to be called
var generatedKey = immediatelyAvailableReference.key;
}
Using Promise interface
//
addBear(){
var immediatelyAvailableReference = base.push('bears', {
data: {name: 'George', type: 'Grizzly'}
}).then(newLocation => {
var generatedKey = newLocation.key;
}).catch(err => {
//handle error
});
//available immediately, you don't have to wait for the Promise to resolve
var generatedKey = immediatelyAvailableReference.key;
}
Allows you to update data at a Firebase endpoint changing only the properties you pass to it.
Warning: calling update with options.data
being null will remove all the data at that endpoint
A Firebase Promise which resolves when the write is complete and rejects if there is an error
Using callback
// bears endpoint currently holds the object { name: 'Bill', type: 'Grizzly' }
base.update('bears', {
data: { name: 'George' },
then(err) {
if (!err) {
Router.transitionTo('dashboard');
//bears endpint is now {name: 'George', type: 'Grizzly'}
}
}
});
Using Promise
// bears endpoint currently holds the object { name: 'Bill', type: 'Grizzly' }
base
.update('bears', {
data: { name: 'George' }
})
.then(() => {
Router.transitionTo('dashboard');
})
.catch(err => {
//handle error
});
Allows you to delete all data at the endpoint location
A Firebase Promise which resolves when the deletion is complete and rejects if there is an error
Using callback
base.remove('bears', function(err) {
if (!err) {
Router.transitionTo('dashboard');
}
});
Using Promise
base
.remove('bears')
.then(() => {
Router.transitionTo('dashboard');
})
.catch(error => {
//handle error
});
Clean up a listener. Listeners are automatically cleaned up when components unmount, however if you wish to remove a listener while the component is still mounted this will allow you to do that. An example would be if you want to start listening to a new endpoint in response to a prop change.
syncState
, bindToState
, or listenTo
No return value
componentDidMount(){
this.ref = base.syncState('users', {
context: this,
state: 'users'
});
}
componentWillUnmount(){
base.removeBinding(this.ref);
}
Removes every Firebase listener and resets all private variables.
No Arguments
No return value
Use the query option to utilize the Firebase Query API. For a list of available queries and how they work, see the Firebase docs.
Queries are accepted in the options
object of each read method (syncState
, bindToState
, listenTo
, and fetch
). The object should have one or more keys of the type of query you wish to run, with the value being the value for the query. For example:
base.syncState('users', {
context: this,
state: 'users',
asArray: true,
queries: {
orderByChild: 'iq',
limitToLast: 3
}
});
The binding above will sort the users
endpoint by iq, retrieve the last three (or, three with highest iq), and bind it to the component's users
state. NOTE: This query is happening within Firebase. The only data that will be retrieved are the three users with the highest iq.
Accepts an initialized firebase database object
An instance of re-base (with the Firestore methods available)
var Rebase = require('re-base');
var firebase = require('firebase');
require('firebase/firestore');
var app = firebase.initializeApp({
apiKey: 'apiKey',
authDomain: 'projectId.firebaseapp.com',
databaseURL: 'https://databaseName.firebaseio.com',
storageBucket: 'bucket.appspot.com',
messagingSenderId: 'xxxxxxxxxxxxxx'
});
var firestore = app.firestore();
var settings = { timestampsInSnapshots: true };
firestore.settings(settings);
var base = Rebase.createClass(firestore);
var Rebase = require('re-base');
var firebase = require('firebase/app');
require('firebase/firestore');
var app = firebase.initializeApp({
apiKey: 'apiKey',
authDomain: 'projectId.firebaseapp.com',
databaseURL: 'https://databaseName.firebaseio.com',
storageBucket: 'bucket.appspot.com',
messagingSenderId: 'xxxxxxxxxxxxxx'
});
var db = firebase.firestore(app);
var settings = { timestampsInSnapshots: true };
db.settings(settings);
var base = Rebase.createClass(db);
This property contains the initialized firebase app that was passed into re-base. You can access any of the firebase services that you are using off this object. For instance, if you are using the auth
service and want to quickly access it off of re-base
This property contains the an object that you can use when adding data that will be converted to a timestamp by Firestore. See the docs for more info.
Bind a document to your component. When then document changes in firestore, your component will re-render with the latest data.
An object which you can pass to removeBinding
if you want to remove the listener while the component is still mounted.
componentWillMount() {
base.bindDoc('myCollection/myDocument', {
context: this,
then() {
this.setState({
loading: false
})
},
onFailure(err) {
//handle error
}
});
}
Listen to a document, when the data changes it will invoke a callback passing it the new data from Firestore.
An object which you can pass to removeBinding
if you want to remove the listener while the component is still mounted.
componentWillMount() {
base.listenToDoc('myCollection/myDocument', {
context: this,
then(data) {
//do something with the data
},
onFailure(err) {
//handle error
}
});
}
Bind a collection to a state property in your component. When then collection changes in firestore, your component will re-render with the latest data.
id
property.ref
property.An object which you can pass to removeBinding
if you want to remove the listener while the component is still mounted.
componentWillMount() {
base.bindCollection('myCollection', {
context: this,
state: 'users',
withRefs: true,
withIds: true,
query: (ref) => ref.where('type', '==', 'subscriber'),
then(data) {
this.setState(state => ({
...state,
loading: false
}))
},
onFailure(err) {
//handle error
}
});
}
Listen to a collection, when the data changes it will invoke a callback passing it the new data from Firestore.
id
property.ref
property.An object which you can pass to removeBinding
if you want to remove the listener while the component is still mounted.
componentWillMount() {
base.listenToCollection('myCollection', {
context: this,
query: (ref) => ref.where('type', '==', 'subscriber'),
then(data) {
// do something with the data
},
onFailure(err) {
//handle error
}
});
}
Fetch either a Collection or Document.
id
property.ref
property.A Promise thats resolve with the resulting data or rejects if the document/collection does not exist or there are any read errors.
componentWillMount() {
base.get(collectionRef, {
context: this,
query: (ref) => ref.where('type', '==', 'subscriber'),
}).then(data => {
//do something with data
}).catch(err => {
//handle error
})
}
Add a new Document to a Collection.
A Promise that resolves when the write is complete or rejects with any error such as a permissions error.
componentWillMount() {
base.addToCollection('myCollection', data, 'myCustomId')
.then(() => {
//document is added to the collection
}).catch(err => {
//handle error
});
}
Update an existing document
A Promise thats resolve when the write is complete or rejects with any error such as a permissions error.
componentWillMount() {
base.updateDoc('myCollection/myDoc', data)
.then(() => {
//document is updated
}).catch(err => {
//handle error
});
}
Deletes a document
A Promise thats resolve when the document is delete or rejects with any error such as a permissions error
componentWillMount() {
base.removeDoc('myCollection/myDoc')
.then(() => {
//document is deleted
}).catch(err => {
//handle error
});
}
Removes documents from a collection. If no query is supplied, it will remove all the documents. If a query is supplied, it will only remove documents that match the query.
A Promise thats resolve when the write is complete or rejects with any error such as a permissions error.
componentWillMount() {
base.bindCollection('myCollection', {
context: this,
state: 'users',
withRefs: true,
withIds: true,
query: (ref) => ref.where('type', '==', 'subscriber'),
then(data) {
this.setState(state => ({
...state,
loading: false
}))
},
onFailure(err) {
//handle error
}
});
}
Syncs a component's local state with a document in Firestore.
An object which you can pass to removeBinding
if you want to remove the listener while the component is still mounted.
componentWillMount() {
base.syncDoc('myCollection/myDoc', data)
.then(() => {
//document is updated
}).catch(err => {
//handle error
});
}
Clean up a listener. Listeners are automatically cleaned up when components unmount, however if you wish to remove a listener while the component is still mounted this will allow you to do that. An example would be if you want to start listening to a new document or change a query on all collection in response to a prop change.
listenToCollection
, bindCollection
, listenToDoc
,bindDoc
or syncDoc
No return value
componentDidMount(){
this.ref = base.syncDoc('users/user-1-doc',
context: this,
state: 'users'
});
}
componentWillUnmount(){
base.removeBinding(this.ref);
}
Removes every Firestore listener.
No Arguments
No return value
Use the query option to utilize the Firestore Query API. For a list of available queries and how they work, see the Firestore docs.
Queries are accepted in the options
object of each collection read method (listenToCollection
, bindCollection
, get(Collection)
). You can also use them with removeFromCollection
to remove documents that match the query
The query options takes a function that will receive the collection reference as its only argument. You can then apply any of the available methods you with to run and must return the reference.
base.bindCollection('users', {
state: 'users',
context: this,
query: ref => {
return ref
.where('type', '==', 'subscriber')
.where('joined', '>', yesterday)
.orderBy('joined');
}
});
4.x no longer defines firebase as a direct dependency but rather a peerDependency. You need to install firebase and handle updates in your own project.
3.x no longer requires you to include the full Firebase SDK in your app. This means that you need to include the parts of Firebase SDK you wish to use and handle initialization of the firebase services in your app instead of re-base doing this for you. re-base only requires you pass it the initialized database service. This also means that the authentication helpers are deprecated and re-base no longer exposes the firebase services.
3.x also removes listeners automatically for you on componentWillUnmount
so you don't have to explicitly call removeBinding
. removeBinding
is still available if you need to remove a listener while the component is still mounted.
For instance, if you are adding and removing listeners in response to a prop change.
To help with migrating to 3.x please see the Migration Guide for the equivalent Firebase SDK methods to use for the deprecated auth helpers.
Changes your re-base initialization:
Change this....
var Rebase = require('re-base');
var base = Rebase.createClass({
apiKey: 'apiKey',
authDomain: 'projectId.firebaseapp.com',
databaseURL: 'https://databaseName.firebaseio.com',
storageBucket: 'bucket.appspot.com'
});
To this...
var Rebase = require('re-base');
var firebase = require('firebase');
var app = firebase.initializeApp({
apiKey: 'apiKey',
authDomain: 'projectId.firebaseapp.com',
databaseURL: 'https://databaseName.firebaseio.com',
storageBucket: 'bucket.appspot.com'
});
var base = Rebase.createClass(app.database());
No changes. Your existing code should work.
Deprecated Methods
base.resetPassword
base.createUser
base.authWithPassword
base.authWithCustomToken
base.authWithOAuthPopup
base.getOAuthRedirectResult
base.authWithOAuthToken
base.authWithOAuthRedirect
base.onAuth
base.unauth
base.getAuth
npm install
tests/specs
src
npm test
re-base is inspired by ReactFire from Firebase. Jacob Turner is also a core contributor and this wouldn't have been possible without his assistance.
MIT