Giter VIP home page Giter VIP logo

re-base's Introduction

re-base

Build Status Coverage Status

welcome

Questions? Find me on twitter at @tylermcginnis

What is re-base?

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.

Why re-base?

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.

Installing

$ npm install --save re-base firebase

Examples

For more in depth examples, see the examples folder.

API

Firebase Real Time Database

Overview

  • 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. When your Firebase endpoint changes, the property on your state will update as well.
  • listenTo: When 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 a Firebase listener before the component unmounts if you need to. (Listeners are automatically cleaned up when component unmounts)
  • reset: Removes all of the Firebase listeners.

createClass(firebaseDatabase)

Purpose

Accepts an initialized firebase database object

Arguments
  1. initialized firebase database (Object)
Return Value

An instance of re-base.

Example using all of firebase
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());
Example using only the firebase database component
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);

initializedApp

Purpose

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.


timestamp

Purpose

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.


syncState(endpoint, options)

Purpose

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.

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint to which you'd like to bind your component's state
  2. options (Object)
    • context: (Object - required) The context of your component
    • state: (String - required) The state property you want to sync with Firebase; can be an arbitrarily nested property a lร  foo.bar
    • defaultValue: (String|Boolean|Number|Object - optional) A default value to set when the Firebase endpoint has no value (i.e., on init) (use this if you want a value other than an empty object or empty array)
    • asArray: (Boolean - optional) Returns the Firebase data at the specified endpoint as an Array instead of an Object
    • 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.
Return Value

An object which you can pass to removeBinding if you want to remove the listener while the component is still mounted.

Example
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
  });
}

bindToState(endpoint, options)

Purpose

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.

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint that you'd like to bind to your component's state
  2. options (Object)
    • context: (Object - required) The context of your component
    • state: (String - required) The state property you want to sync with Firebase; can be an arbitrarily nested property a lร  foo.bar (no arrays)
    • 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.
Return Value

An object which you can pass to removeBinding if you want to remove the listener while the component is still mounted.

Example
componentDidMount(){
  base.bindToState('tasks', {
    context: this,
    state: 'tasks',
    asArray: true
  });
}

listenTo(endpoint, options)

Purpose

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.

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint which contains the data with which you'd like to invoke your callback function
  2. options (Object)
    • 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.
Return Value

An object which you can pass to removeBinding when your component unmounts to remove the Firebase listeners.

Example
componentDidMount(){
  base.listenTo('votes', {
    context: this,
    asArray: true,
    then(votesData){
      var total = 0;
      votesData.forEach((vote, index) => {
        total += vote
      });
      this.setState({total});
    }
  })
}

fetch(endpoint, options)

Purpose

Allows you to retrieve the data from a Firebase endpoint just once without subscribing or listening for data changes.

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint which contains the data you're wanting to fetch
  2. options (Object)
    • context: (Object - optional) 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 - optional) 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.
Return Value

A Firebase Promise which resolves when the write is complete and rejects if there is an error

Example

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
  })
}

post(endpoint, options)

Purpose

Allows you to update a Firebase endpoint with new data. Replace all the data at this endpoint with the new data

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint that you'd like to update with the new data
  2. options (Object)
    • 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.
Return Value

A Firebase Promise which resolves when the write is complete and rejects if there is an error

Example

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
  });
}

push(endpoint, options)

Purpose

Allows you to add data to a Firebase endpoint. Adds data to a child of the endpoint with a new Firebase push key

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint that you'd like to push the new data to
  2. options (Object)
    • 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.
Return Value

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."

Example

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;
}

update(endpoint, options)

Purpose

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

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint that you'd like to update
  2. options (Object)
    • 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.
Return Value

A Firebase Promise which resolves when the write is complete and rejects if there is an error

Example

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
  });

remove(endpoint, callback)

Purpose

Allows you to delete all data at the endpoint location

Arguments
  1. endpoint (String)
    • The relative Firebase endpoint that you'd like to delete data from
  2. callback (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.
Return Value

A Firebase Promise which resolves when the deletion is complete and rejects if there is an error

Example

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
  });

removeBinding(ref)

Purpose

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.

Arguments
  1. ref (Object)
    • The return value of syncState, bindToState, or listenTo
Return Value

No return value

Example
componentDidMount(){
  this.ref = base.syncState('users', {
    context: this,
    state: 'users'
  });
}
componentWillUnmount(){
  base.removeBinding(this.ref);
}

reset()

Purpose

Removes every Firebase listener and resets all private variables.

Arguments

No Arguments

Return Value

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.

Firestore

Overview

  • createClass : Initialize the re-base instance
  • bindDoc : One way data binding. When your Document changes, your component will update with the new data.
  • listenToDoc : One way data binding. When your Document changes, it will invoke a callback passing it the new data.
  • bindCollection : One way binding. When the collection changes, your component will update with the new data.
  • listenToCollection: One way binding. When the collection changes, it will invoke a callback passing it the new data.
  • get : Fetch either a Collection or Document.
  • addToCollection : Add a new document to a collection.
  • updateDoc : Add a new document to a collection.
  • removeDoc : Deletes a document.
  • removeFromCollection : Deletes documents from a collection.
  • syncDoc : Syncs a components local state with a document (Read and Write)
  • removeBinding: Remove a Firebase listener before the component unmounts if you need to. (Listeners are automatically cleaned up when component unmounts)
  • reset: Removes all Firestore listeners.

createClass(firestoreDatabase)

Purpose

Accepts an initialized firebase database object

Arguments
  1. initialized firestore database (Object)
Return Value

An instance of re-base (with the Firestore methods available)

Example using all of firebase
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);
Example using only the firestore component
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);

initializedApp

Purpose

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


timestamp

Purpose

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.


Reading Data

bindDoc(refOrPath, options)

Purpose

Bind a document to your component. When then document changes in firestore, your component will re-render with the latest data.

Arguments
  • DocumentReference or path (DocumentReference or String)
  • options (Object)
    • context: (Object - required) your react component
    • state: (String - optional) a property name on your state to bind your document to, if omitted the document will be merged into your existing state
    • then: (Function - optional) a callback that will be called when the listener is set, use for loading indicators
    • onFailure: (Function - optional) a callback that will be called with any errors such as permissions errors
Return Value

An object which you can pass to removeBinding if you want to remove the listener while the component is still mounted.

Example
componentWillMount() {
  base.bindDoc('myCollection/myDocument', {
    context: this,
    then() {
      this.setState({
        loading: false
      })
    },
    onFailure(err) {
      //handle error
    }
  });
}

listenToDoc(refOrPath, options)

Purpose

Listen to a document, when the data changes it will invoke a callback passing it the new data from Firestore.

Arguments
  1. DocumentReference or path (DocumentReference or String)
  2. options (Object)
    • context: (Object - required) your react component
    • then: (Function - required) a callback that will be called with the data from Firestore
    • onFailure: (Function - optional) a callback that will be called with any errors such as permissions errors
Return Value

An object which you can pass to removeBinding if you want to remove the listener while the component is still mounted.

Example
componentWillMount() {
  base.listenToDoc('myCollection/myDocument', {
    context: this,
    then(data) {
      //do something with the data
    },
    onFailure(err) {
      //handle error
    }
  });
}

bindCollection(refOrPath, options)

Purpose

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.

Arguments
  1. CollectionReference or path (DocumentReference or String)
  2. options (Object)
    • context : (Object - required) your react component
    • state : (String - required) the state property to bind the collection to.
    • query : (Function - optional) a function that receives the created ref as its only argument. You can chain any Firestore queries you want to perform. See Using Firestore Queries
    • withIds : (Boolean - optional) will embed firestore generated document ids inside each document in your collections on the id property.
    • withRefs : (Boolean - optional) will embed the DocumentReference inside each document in your collection on the ref property.
    • then : (Function - optional) a callback that will be called when the listener is set, use for loading indicators
    • onFailure : (Function - optional) a callback that will be called with any errors such as permissions errors
Return Value

An object which you can pass to removeBinding if you want to remove the listener while the component is still mounted.

Example
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
    }
  });
}

listenToCollection(refOrPath, options)

Purpose

Listen to a collection, when the data changes it will invoke a callback passing it the new data from Firestore.

Arguments
  1. CollectionReference or path (CollectionReference or String)
  2. options (Object)
    • context : (Object - required) your react component
    • then : (Function - required) a callback that will be called with the data
    • query : (Function - optional) a function that receives the created ref as its only argument. You can chain any Firestore queries you want to perform. See Using Firestore Queries
    • withIds : (Boolean - optional) will embed firestore generated document ids inside each document in your collections on the id property.
    • withRefs : (Boolean - optional) will embed the DocumentReference inside each document in your collection on the ref property.
    • onFailure : (Function - optional) a callback that will be called with any errors such as permissions errors
Return Value

An object which you can pass to removeBinding if you want to remove the listener while the component is still mounted.

Example
componentWillMount() {
  base.listenToCollection('myCollection', {
    context: this,
    query: (ref) => ref.where('type', '==', 'subscriber'),
    then(data) {
     // do something with the data
    },
    onFailure(err) {
      //handle error
    }
  });
}

get(refOrPath, options)

Purpose

Fetch either a Collection or Document.

Arguments
  1. CollectionReference, DocumentReference or path (CollectionReference, DocumentReference or String)
  2. options (Object) only available on collections.
    • query : (Function - optional) a function that receives the created ref
    • withIds : (Boolean - optional) will embed firestore generated document ids inside each document in your collections on the id property.
    • withRefs : (Boolean - optional) will embed the DocumentReference inside each document in your collection on the ref property.
Return Value

A Promise thats resolve with the resulting data or rejects if the document/collection does not exist or there are any read errors.

Example
componentWillMount() {
  base.get(collectionRef, {
    context: this,
    query: (ref) => ref.where('type', '==', 'subscriber'),
  }).then(data => {
    //do something with data
  }).catch(err => {
    //handle error
  })
}

Writing Data

addToCollection(refOrPath, data, id)

Purpose

Add a new Document to a Collection.

Arguments
  1. CollectionReference or path (CollectionReference or String)
  2. Data (Object) the document data
  3. ID (String - optional) the id for the document. If omitted, the Firestore will generate an id for you.
Return Value

A Promise that resolves when the write is complete or rejects with any error such as a permissions error.

Example
componentWillMount() {
  base.addToCollection('myCollection', data, 'myCustomId')
    .then(() => {
      //document is added to the collection
    }).catch(err => {
    //handle error
  });
}

updateDoc(refOrPath, data)

Purpose

Update an existing document

Arguments
  1. DocumentReference or path (DocumentReference or String)
  2. Data (Object) the document data
Return Value

A Promise thats resolve when the write is complete or rejects with any error such as a permissions error.

Example
componentWillMount() {
  base.updateDoc('myCollection/myDoc', data)
    .then(() => {
      //document is updated
    }).catch(err => {
    //handle error
  });
}

removeDoc(refOrPath)

Purpose

Deletes a document

Arguments
  1. DocumentReference or path (DocumentReference or String)
Return Value

A Promise thats resolve when the document is delete or rejects with any error such as a permissions error

Example
componentWillMount() {
  base.removeDoc('myCollection/myDoc')
    .then(() => {
      //document is deleted
    }).catch(err => {
    //handle error
  });
}

removeFromCollection(refOrPath, options)

Purpose

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.

Arguments
  1. CollectionReference or path (CollectionReference or String)
  2. options (Object)
    • query : (Function - optional) a function that receives the created ref as its only argument. You can chain any Firestore queries you want to perform. See Using Firestore Queries
Return Value

A Promise thats resolve when the write is complete or rejects with any error such as a permissions error.

Example
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
    }
  });
}

Sync Data

syncDoc(refOrPath, options)

Purpose

Syncs a component's local state with a document in Firestore.

Arguments
  1. DocumentReference or path (DocumentReference or String)
  2. options (Object)
    • context : (Object - required) your react component
    • state : (String - required) the state property to sync
    • then : (Function - optional) a callback that will be called when the listener is set, use for loading indicators
    • onFailure : (Function - optional) a callback that will be called with any errors such as permissions errors
Return Value

An object which you can pass to removeBinding if you want to remove the listener while the component is still mounted.

Example
componentWillMount() {
  base.syncDoc('myCollection/myDoc', data)
    .then(() => {
      //document is updated
    }).catch(err => {
    //handle error
  });
}

Remove Listeners

Purpose

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.

Arguments
  1. ref (Object)
  • The return value of listenToCollection, bindCollection, listenToDoc,bindDoc or syncDoc
Return Value

No return value

Example
componentDidMount(){
  this.ref = base.syncDoc('users/user-1-doc',
    context: this,
    state: 'users'
  });
}
componentWillUnmount(){
  base.removeBinding(this.ref);
}

Purpose

Removes every Firestore listener.

Arguments

No Arguments

Return Value

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');
  }
});

Major Changes:

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.

Major Changes:

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());

Changes to Database methods


No changes. Your existing code should work.


Changes to Authentication methods


Deprecated Methods

base.resetPassword base.createUser base.authWithPassword base.authWithCustomToken base.authWithOAuthPopup base.getOAuthRedirectResult base.authWithOAuthToken base.authWithOAuthRedirect base.onAuth base.unauth base.getAuth

Contributing

  1. npm install
  2. Add/edit tests in tests/specs
  3. Add/edit source in src
  4. npm test

Credits

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.

License

MIT

re-base's People

Contributors

arve0 avatar bdougie avatar benthemonkey avatar bfollington avatar bgates avatar chentsulin avatar clarle avatar dswaby avatar flagello avatar foffer avatar golmansax avatar hermawan22 avatar jacob-israel-turner avatar jamiemchale avatar marchon avatar markspolakovs avatar milushov avatar mosse avatar nybblr avatar ojame avatar patrickduncan avatar pjvds avatar qwales1 avatar reflog avatar sacret avatar samit4me avatar tylermcginnis avatar vjeux avatar wung-s avatar zbuttram avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

re-base's Issues

Dynamic state based on firebase value

I've got a value of a "current" thing in firebase. That can, and does change.

Now, I've got a list of all possible things.

What I cannot get my head around is how do I set up a dynamic query of the things based on the "current" things id??

Here's my componentWillMount, everything works great, except the "question" one. It looks like this.state.activeQuestion is just null. If I put a static string in there at the end of that resource, like 'questions/0', it works fine.

  componentWillMount() {
    base.syncState('activeQuestion', {
      context: this,
      state: 'activeQuestion'
    });

    base.bindToState('questions', {
      context: this,
      state: 'questions',
      asArray: true
    });

    base.bindToState('questions/' + this.state.activeQuestion, {
      context: this,
      state: 'question'
    });
  };

I'm trying to get this question to pass to a child component.

Thanks for any help you can give me, Cheers!

Concurrency question

Hi there,

If I want to use Firebase just as a datastore in some cases... so just pushing updates out to Firebase... but not maintaining a live connection since I don't need it and don't want to add to my concurrency count... is that possible with Re-Base.

So could I just do:

var Rebase = require('re-base');
var base = Rebase.createClass('https://myapp.firebaseio.com');
addUser(){
base.post('users/${userId}', {
data: {name: 'Tyler McGinnis', age: 25},
then(){
Router.transitionTo('dashboard');
}
});
}

And not worry about this adding towards my concurrent user count, as I'm not actively Syncing State?

syncState then() infinite loop

I'm having an issue with using the callback supplied to syncState. In componentDidMount I need to call a component method which modifies the state of nodes. As seen in this simplified example, I need the nodes from firebase before calling this method, so I call this.addNode in the syncState callback.

However, this is causing an infinite loop. It seems that the syncState callback is being called each time setState is used in addNode. Isn't the callback only meant to be called once when re-base syncs the state with firebase?

Note: if I wrap this.addNode() in a setTimeout, there's no infinite loop.

class TestNodes extends Component {
  state = {
    nodes: []
  }

  componentDidMount(){
    const nodes = base.syncState('nodes', {
      context: this,
      state: 'nodes',
      asArray: true,
      then() {
        // THIS IS THE LINE
        this.addNode();
      }
    });
    this.syncs = [nodes];
  }
  componentWillUnmount(){
    this.syncs.forEach(ref => base.removeBinding(ref));
  }

  addNode = () => {
    const newNodes = this.state.nodes.concat([{test:123}]);
    this.setState({
      nodes: newNodes
    });
  }

  render(){
    return (
      <div>
        {this.state.nodes.map((node, index) => <span key={index}>{node.test}</span>)}
      </div>
    )
  }
}

More examples please

In the case to implement user authentication, sign in & sign up, for instance. How should I structure the user model and controller to work with this library? It seems not designed to replace the dispatch/action/reducer patterns in Flux/Redux...

Add error callback

ref.on takes a second, onFailure callback. It should be integrated into rebase.

Expose 'error' callback

Opened as a result of #65.

I just glanced over the code and it turns out we aren't exposing the error callback, at least for syncState. It looks like when using post, you can watch for an error argument in the .then callback. But we should implement across-the-board error callbacks.

'then' method on 'listenTo' options object should be invoked with proper context

Currently, if you try to run this.setState in the then method on the configuration object for listenTo, it will throw an error. When the then method, it should invoke it with the proper context. IE, this:

base.listenTo('data', {
  context: this,
  then: function(data){
    console.log(data);
    // login, manipulate data, etc.
    this.setState({
      data: data
    });
  }.bind(this)
})

could be invoked like this:

base.listenTo('data', {
  context: this,
  then(data){
    console.log(data);
    // login, manipulate data, etc.
    this.setState({
      data: data
    });
  }
})

Underscore in ref name doesn't show any results for syncState

Is there any reason that having an underscore character in the ref name / path name would interfere with the syncState function?

In my case, this yields an empty array:

base.syncState(`room_messages/${roomId}`,
context: this,
  state: 'messages',
  asArray: true
});

However, moving the contents of room_messages to room-messages and changing to base.syncState(room-messages/${roomId}, ...) returns the correct messages.

Not a huge deal b/c we can use hyphens or camel case, but would be nice to use underscores as that is our convention otherwise.

Authorization

It would be cool to add an authorization option too to this project,
like authWithPassword in Firebase.

Can't reconnect to new endpoint

Setup: We have an app that lets multiple clients hook in on one shared session. It's an app that's being used in classrooms so the sessions are stored as classes. In the app students can specify the class they're connecting to through an ID.

When a student enters an ID I connect to that class:

this.base = Rebase.createClass('https://[my-url].firebaseio.com/classes/' + classId);

The problem arrises when the student enters a wrong ID. When they change the ID, I try to recreate the Rebase connection with a different ID:

delete this.base
this.base = Rebase.createClass('https://[my-url].firebaseio.com/classes/' + classId);

But all calls are still directed at the initial endpoint. I think singleton-ing Rebase wasn't that great an idea. You'd actually want to instantiate it for each Rebase.createClass call.

Any good way to validate form using re-base?

Hi there,

Would like to check with you if there are any good patterns to do form validation with this library?
I know that we can set some constraints on firebase but how do we actually do the validation..

I've seen this but we do not actually have a model to deal with.. ๐Ÿ˜ญ

Sorry to be asking a question here, I thought it would be more appropriate to ask it here as there's not much re-base questions on stackoverflow.. mostly about git rebase..

Anyway, great work with your library! ๐Ÿ‘ it's awesome.

Rethink API

I think we should change the then property in our options object to either a callback parameter on its own or promisify the method itself.

It would also look cleaner to include the endpoint in the options object.

E.G.

var options = {
  context: this,
  asArray: true,
  endpoint:  `users/${user_id}/info`
}


base.listenTo(options, (users) => {
  // With callback
})

base.listenTo(options)
  .then((users) => /* With promise */)

How to push update on initial page load?

I'm trying to implement a view counter which will update the components count once after the initial state loads while using syncState and I'm unable to utilize the component's state unless I use fetch.

Here's part of my code:

getInitialState : function() {
    return {
      snippetInfo : {}
    }
  },
  componentWillMount : function() {
    this.ref = base.syncState('/snippet/' + this.props.params.snippetId, {
      context : this,
      state: 'snippetInfo'
    });
  },
  componentDidMount : function() {
    var snippetId = this.props.params.snippetId;
    var snippetViews = this.state.snippetInfo.views;

    if(snippetViews) {
      base.post('/snippet/' + snippetId + '/views', {
        data: snippetViews + 1
      });
    }
  },
 componentWillUnmount : function() {
    base.removeBinding(this.ref);
 },

My issue here is the state object snippetInfo is empty so this.state.snippetInfo.views becomes undefined. If I change syncState to fetch and manually set the state with the data returned it works fine:

  componentWillMount : function() {
    this.ref = base.fetch('/snippet/' + this.props.params.snippetId, {
      context : this,
      then(data){
        this.state.snippetInfo = data;
        this.updateViews();
     }
    });
  },

The issue here is then it's not going to update the views "live" -- I want the page to update the view counter if other sessions hit the same page, hence using syncState. How do I correctly hook into the lifecycle so that I can fire off the view update ONCE on page load? If I hook into the then callback for syncState, it'll continuously update the view counter since it's continuously syncing the state. Really confused.

Passing callbacks to bindToState

Apologies if I'm missing something, but is there any reason why you couldn't pass a callback to bindToState when the listener is set, as with syncState?

Love your work Tyler!

can't call syncState on two different objects

I wanted to sync state on two different objects like:

this.ref = base.syncState('teams/' + this.props.params.id.toString(), {
            context: this,
            state: 'team'
        });
this.ref = base.syncState('players/' + this.props.params.id.toString(), {
            context: this,
            state: 'players'
        });

But this doesn't work. "team" is not being synced but players are. It's like it's being overwritten.

post needs a unique ID?

Hello,
I tried to do a post to my firebase and it wiped out all of the data I had in there (not much but it was unexpected). I was just doing something like this:

this.base.post('tools', {
    data: {name: 'test', type: 'test type'},
    then() {
        console.log('complete!');
    }
});

I was expecting, because I didn't pass an ID, that the tool would be added with a unique ID rather than wipe everything out and replace it. Do I have to pass a unique ID I create myself to the endpoint? Why can it not create a unique ID for me when posting? Or am I just missing something from the API?

I'm new to both React and Firebase so maybe I have a fundamental misunderstanding of how all this works but any light you could shed would be greatly appreciated.

GraphQL Integration

First off, re-base is a library that has been a long time coming, fantastic to learn about it and thanks!

I posted a few comments on a Firebase issue about GraphQL support in the past few days. Are there any plans to extend re-base in such a direction? Here are my suggestions:

FirebaseExtended/reactfire#38

fetch doesn't require context option

It appears that the required context option of fetch is not used, and hence shouldn't be required. If you pass in an empty object and print it out in the then() callback, it's still an empty object, and there's no other way to interact with the fetch function in which the context would be relevant (AFAIK).

Firebase Authentication and State Issue

Hello, I am a student learning React and am using re-base, which I love so far. Great job with it and thanks! :)

I am trying to create a web app where a user must sign in with Google before viewing any of the content. I began with the code from the github-notetaker example and have edited Main.js. So far, I am writing the entire logic inside the init() method and setting loggedIn and email values in this.state.

class Main extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      loggedIn : false,
      email: ''
    };
  }
  init(){
    console.log('Init called');
    var controller = this; // I think this may be a problem line

    this.ref = new Firebase('https://myapp.firebaseio.com/');
    var currentAuthData = this.ref.getAuth();
    if (currentAuthData) {
      console.log('User is already logged in.'); // FIRST PRINT
      console.log(currentAuthData["google"]); // SECOND PRINT
      this.setState({
        loggedIn: true,
        email: currentAuthData["google"]["email"]
      });
      console.log(this.state.email); // THIRD PRINT
    } else {

      this.setState({
        loggedIn: false
      });
      console.log('Attempting to authenticate user account');
      this.ref.authWithOAuthPopup('google', function (error, authData) {
        if (error) {
          console.log('Login Failed!', error);
        } else {
          console.log('Authenticated successfully');
          controller.init(); // I think this may be a problem line
        }
      }, {
        scope: "email"
      });
    }
  }

I am encountering an odd bug and believe that I am doing this the wrong way. When the user authenticates successfully, all of the information is saved and the user's email is written. Here's what the console log looks like:

User is already logged in.
[Object] // corresponds to authData.google result returned by authentication
[email protected] // correct!

However, when I refresh the page, the new print looks like this, and it appears that the email and object are still available to me but are not getting saved in this.state.

User is already logged in.
[Object] // corresponds to authData.google result returned by authentication
null

I am currently learning React and so this may be a really simple error. Thank you so much for your time and help in advance. If you could point me in the right direction or let me a know a better way to do this, I'd really appreciate it. Thanks!

Push should return the endpoint it just created?

The Firebase one does. Something like the below should do maybe?

function _push(endpoint, options){
    _validateEndpoint(endpoint);
    optionValidators.data(options);
    var ref = new Firebase(`${baseUrl}/${endpoint}`);
    var returnEndpoint;
    if(options.then){
      returnEndpoint = ref.push(options.data, options.then);
    } else {
      returnEndpoint = ref.push(options.data);
    }
    return returnEndpoint;
  };

Offline

I am curious how this two way binding is supposed to work with offline app state. I think what we need is sync with Redux support. Having magic state black box is step back I suppose.

DRY code

  • Break out 'add event listener' function.

Using Queries with SyncState

Hello, I am currently using the syncState method and modifying the github-notetaker example. Here's how my Firebase is structured:

MyApp
- posts
- users
    1 -
        username: "Felix",
        access: "normal"
    2 -
        username: "Bob",
        access: "normal"
    3 -
        username: "Alice",
        access: "banned"

When loading a list of users, I only want to fetch the ones that have access="normal".

Here's my Firebase code, which I think should be correct unless I am making a dumb mistake.

var base = Rebase.createClass('https://MyApp.firebaseio.com/');

class ProfileView extends React.Component {
...
init() {
  this.ref = base.syncState('users', {
      context: this,
      asArray: true,
      state: 'users',
      queries: {
        orderByChild: 'access',
        equalTo: 'normal'
      }
  });
componentWillMount(){
    this.router = this.context.router;
  }
  componentDidMount(){
    this.init();
  }
  componentWillUnmount(){
    base.removeBinding(this.ref);
  }
  componentWillReceiveProps(){
    base.removeBinding(this.ref);
    this.init();
  }
...

I am then printing out the usernames onto the page, which is working correctly. However, regardless of what I put into the queries argument, the same number of results are returned.

Am I doing this correctly? If not, what are some things that may be causing this? Thank you so much!

Accessing localStorage from within syncState callback

Hi guys. Great library.

I'm trying to do the following:

  componentDidMount : function() {

     // POINT A
    var localStorageRefA = localStorage.getItem('order-' + this.props.params.storeId);

    base.syncState(this.props.params.storeId + '/fishes', {
      context : this,
      state : 'fishes',
      then() {

        // POINT B
        var localStorageRefB = localStorage.getItem('order-' + this.props.params.storeId); 

        if (localStorageRefA) {
          // update our component state to reflect what is in localStorage
          this.setState({ order : JSON.parse(localStorageRef) });
        }
      }
    })    
  },

At POINT A which is called before the callback, the item stored at that key in localStorage is recognised, yet at POINT B it is not accessible, though localStorageRefA and the object returned from localStorage at that point, is.

Possibly a fundamental misunderstanding on my part. Any help / advice is very much appreciated.
Thanks in advance.

Listen to URL params

Hi,

is it possible to listen to the change in URL params like

(a) http://localhost:3000/build/index.html#/profile/bill

(b) http://localhost:3000/build/index.html#/profile/john

and re-fetch the data? Something like:

    componentDidMount() {
        this.ref = this.base.syncState(this.props.params.username, {
            context: this,
            state: 'notes',
            asArray: true
        });
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.