Questions? Find me on twitter at @tylermcginnis33
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.
- syncState: Two way data binding between any property on your component's state and any endpoint in Firebase. Use the same API you're used to to update your component's state (setState), and Firebase will also update.
- bindToState: One way data binding. Whenever your Firebase endpoint changes, the property on your state will update as well.
- listenTo: Whenever your Firebase endpoint changes, it will invoke a callback passing it the new data from Firebase.
- fetch: Retrieve data from Firebase without setting up any binding or listeners.
- post: Add new data to Firebase.
- push: Push new child data to Firebase.
- update: Update child data using only the referenced properties
- remove: Remove data from Firebase
- removeBinding: Remove all of the Firebase listeners when your component unmounts.
- reset: Removes all of the Firebase listeners and resets the re-base instance (for testing purposes).
- Firebase Services Exposes the firebase services directly if you want to use anything that re-base does not provide a helper function for
$ npm install re-base
For more in depth examples of the API, see the examples
folder.
Accepts a firebase configuration object as the first argument and an optional 'name' for the app as the second
- configuration
- type: object
- properties:
- apiKey (string - required) your firebase API key
- authDomain (string - required) your firebase auth domain
- databaseURL (string - required) your firebase database root URL
- storageBucket (string - optional) your firebase storage bucket
- messagingSenderId: (string - optional) your firebase messaging sender id
- app name - type: string (optional, defaults to '[DEFAULT]')
An instance of re-base.
var Rebase = require('re-base');
var base = Rebase.createClass({
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com",
storageBucket: "bucket.appspot.com",
messagingSenderId: "xxxxxxxxxxxxxx"
}, 'myApp');
##delete(callback)
Deletes the instance of re-base returned from Rebase.createClass
, removing all the listeners that were added by the instance, and the underlying firebase app that was created.
Note: You cannot re-initialize an app of the same name after it has been deleted.
- callback
- type: (function - optional)
- function that is called when the app has been deleted
var Rebase = require('re-base');
var myApp = Rebase.createClass({
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com",
storageBucket: "bucket.appspot.com",
}, 'myApp');
myApp.delete(() => {
//app has been deleted
});
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.
- endpoint - type: string - The relative Firebase endpoint to which you'd like to bind your component's state
- options
- type: object
- properties:
- context: (object - required) The context of your component
- state: (string - required) The state property you want to sync with Firebase
- asArray: (boolean - optional) Returns the Firebase data at the specified endpoint as an Array instead of an Object
- isNullable: (boolean - optional) Sets state as null instead of empty Object or Array if there is no Firebase data
- keepKeys: (boolean - optional) will keep any firebase generated keys intact when manipulating data using the asArray option.
- queries: (object - optional) Queries to be used with your read operations. See Query Options for more details.
- then: (function - optional) The callback function that will be invoked when the initial listener is established with Firebase. Typically used (with syncState) to change
this.state.loading
to false. - onFailure: (function - optional) A callback function that will be invoked if the current user does not have read or write permissions at the location.
An object which you can pass to removeBinding
when your component unmounts to remove the Firebase listeners.
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.
- endpoint - type: string - The relative Firebase endpoint that you'd like your component's state property to listen for changes
- options
- type: object
- properties:
- context: (object - required) The context of your component
- state: (string - required) The state property you want to sync with Firebase
- asArray: (boolean - optional) Returns the Firebase data at the specified endpoint as an Array instead of an Object
- queries: (object - optional) Queries to be used with your read operations. See Query Options for more details.
- then: (function - optional) The callback function that will be invoked when the initial listener is established with Firebase. Typically used (with bindToState) to change
this.state.loading
to false. - onFailure: (function - optional) A callback function that will be invoked if the current user does not have read permissions at the location.
An object which you can pass to removeBinding
when your component unmounts to remove the Firebase listeners.
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.
- endpoint - type: string - The relative Firebase endpoint which contains the data with which you'd like to invoke your callback function
- options
- type: object
- properties:
- context: (object - required) The context of your component
- asArray: (boolean - optional) Returns the Firebase data at the specified endpoint as an Array instead of an Object
- then: (function - required) The callback function that will be invoked with the data from the specified endpoint when the endpoint changes
- onFailure: (function - optional) The callback function that will be invoked if the current user does not have read permissions at the location.
- queries: (object - optional) Queries to be used with your read operations. See Query Options for more details.
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.
- endpoint - type: string - The relative Firebase endpoint which contains the data you're wanting to fetch
- options
- type: object
- properties:
- context: (object - required) The context of your component
- asArray: (boolean - optional) Returns the Firebase data at the specified endpoint as an Array instead of an Object
- then: (function - required) The callback function that will be invoked with the data from the specified endpoint when the endpoint changes
- onFailure: (function - optional) The callback function that will be invoked with an error that occurs reading data from the specified endpoint
- queries: (object - optional) Queries to be used with your read operations. See Query Options for more details.
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
- endpoint - type: string - The relative Firebase endpoint that you'd like to update with the new data
- options
- type: object
- properties:
- data: (any - required) The data you're wanting to persist to Firebase
- then: (function - optional) A callback that will get invoked once the new data has been saved to Firebase. If there is an error, it will be the only argument to this function.
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
- endpoint - type: string - The relative Firebase endpoint that you'd like to push the new data to
- options
- type: object
- properties:
- data: (any - required) The data you're wanting to persist to Firebase
- then: (function - optional) A callback that will get invoked once the new data has been saved to Firebase. If there is an error, it will be the only argument to this function.
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 the all the data at that endpoint
- endpoint - type: string - The relative Firebase endpoint that you'd like to update
- options
- type: object
- properties:
- data: (any - required) The data you're wanting to persist to Firebase
- then: (function - optional) A callback that will get invoked once the new data has been saved to Firebase. If there is an error, it will be the only argument to this function.
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
- endpoint - type: string - The relative Firebase endpoint that you'd like to delete data from
- callback - type: (function - optional) - A callback that will get invoked once the data is successfully removed Firebase. If there is an error, it will be the only argument to this function.
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
});
Remove the listeners to Firebase when your component unmounts.
- ref - type: Object - The return value of 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. Used for testing purposes.
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.
re-base exposes a few methods of the Firebase Auth service to help with user authentication.
Authenticate a user by email and password.
the Email sign-in method needs to be enabled in your firebase console
- authentication object
- type: Object
- properties:
- email (string - required)
- password (string - required)
- auth handler
- type: function
- arguments:
- error (object or null)
- user data (object)
- arguments:
No return value
var authHandler = function(error, user) {
if(error) doSomethingWithError(error);
doSomethingWithUser(user);
}
// Simple email/password authentication
base.authWithPassword({
email : 'bobtony@firebase.com',
password : 'correcthorsebatterystaple'
}, authHandler);
Authenticate a user using an OAuth popup
the sign in provider you are using needs to be enabled in your firebase console
- provider - type: string - name of auth provider "facebook, twitter, github, google"
- auth handler
- type: function
- arguments:
- error (object or null)
- user data (object)
- arguments:
- settings (available settings vary per auth provider)
- type: object (optional)
- properties:
- scope (array or string)
- properties:
No return value
var authHandler = function(error, user) {
if(error) doSomethingWithError(error);
doSomethingWithUser(user);
}
//basic
base.authWithOAuthPopup('twitter', authHandler);
// with settings
base.authWithOAuthPopup('github', authHandler, {scope: ['repos']});
Authenticate a user using an OAuth redirect
the sign in provider you are using needs to be enabled in your firebase console
- provider - type: string - name of auth provider "facebook, twitter, github, google"
- auth handler
- type: function
- arguments:
- error (object or null)
- arguments:
- settings (available settings vary per auth provider)
- type: object (optional)
- properties:
- scope (array or string)
- properties:
No return value
var authHandler = function(error) {
if(error) doSomethingWithError(error);
// noop if redirect is successful
return;
}
//basic
base.authWithOAuthRedirect('twitter', authHandler);
// with settings
base.authWithOAuthRedirect('github', authHandler, {scope: ['repos']});
Completes the OAuth redirect flow initiated by authWithOAuthRedirect
- handler
- type: function
- arguments:
- error (object or null)
- user data (object)
- arguments:
No return value
var authHandler = function(error) {
if(error) console.log(error);
// noop if redirect is successful
return;
}
var onRedirectBack = function(error, authData){
if(error) console.log(error);
if(authData.user){
doSomethingWithAuthenticatedUser(authData.user);
} else {
//redirect to twitter for auth
base.authWithOAuthRedirect('twitter', authHandler);
}
}
base.authGetOAuthRedirectResult(onRedirectBack);
Authenticate with OAuth provider using a token
- provider - type: string - name of auth provider "facebook, twitter, github, google"
- token - type: string
- handler
- type: function
- arguments:
- error (object or null)
- user data (object)
- arguments:
- settings (available settings vary per auth provider)
- type: object (optional)
- properties:
- scope (array or string)
- providerOptions (object)
- properties:
- secret (twitter only - optional)
- idToken(google only - optional, must be null if using accessToken)
- accessToken(google only - optional)
- properties:
- properties:
No return value
var authHandler = function(error, user) {
if(error) doSomethingWithError(error);
doSomethingWithAuthenticatedUser(user);
}
// optional settings for auth provider
var settings = { scope: ['repos'] };
base.authWithOAuthToken('twitter', <yourtoken>, authHandler, settings);
Authenticate OAuth redirect flow initiated by authWithOAuthRedirect
- token - type: string
- auth handler
- type: function
- arguments:
- error (object or null)
- user data (object)
- arguments:
No return value
var authHandler = function(error, user) {
if(error) doSomethingWithError(error);
doSomethingWithAuthenticatedUser(user);
}
base.authWithCustomToken(<yourtoken>, authHandler);
Signs out the currently logged in user
none
No return value
base.unauth()
Listen to the authentication event
- handler
- type: function
- arguments:
- user data (object or null) null if user is not logged in
- arguments:
an unsubscribe function for the added listener
function authDataCallback(user) {
if (user) {
console.log("User " + user.uid + " is logged in with " + user.providerId);
} else {
console.log("User is logged out");
}
}
// Listen to authentication
var unsubscribe = base.onAuth(authDataCallback);
//to remove listener
unsubscribe();
re-base exposes a few helper methods for user methods for user management.
// Create
base.createUser({
email: 'bobtony@firebase.com',
password: 'correcthorsebatterystaple'
}, userHandler);
// Reset Password
base.resetPassword({
email: 'bobtony@firebase.com'
}, errorHandler);
re-base also exposes the firebase services directly if you need them.
Firebase App Docs
base.app
Firebase Database Docs
base.database
Firebase Storage Docs
base.storage
Firebase Auth Docs
base.auth
Firebase Messaging Docs
base.messaging
The initialized Firebase app for the re-base instance
base.initializedApp
Using the default app
var base = Rebase.createClass(configObject);
var databaseService = base.database();
Using another 'named' app
var base = Rebase.createClass(configObject, 'myApp');
var databaseService = base.database(base.initializedApp);
First follow the upgrade guide at https://firebase.google.com/support/guides/firebase-web
Change your re-base initialization to use the new firebase configuration.
Change this....
var Rebase = require('re-base');
var base = Rebase.createClass('https://myapp.firebaseio.com');
To 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",
});
No changes. Your existing code should work.
Deprecated Methods
base.offAuth
base.onAuth
now returns an unsubscribe function that removes the listener.
Behavior Changes
base.authWithOAuthRedirect
The redirect flow needs to be completed with an added base.authGetOAuthRedirectResult
method. See example.
### Changes to User Management
Deprecated Methods
base.removeUser
- users can only remove themselves. See firebase documentation.
base.changePassword
users can only change their own passwords. See firebase documentation.
Behavior Changes
base.createUser
- This method will now log you in as the newly created user on success. See firebase documentation.
npm install
- Edit
src/rebase.js
- Add/edit tests in
tests/specs/re-base.spec.js
npm run build
npm run 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