JSPM

  • Created
  • Published
  • Downloads 2539
  • Score
    100M100P100Q113988F
  • License MIT

Redux bindings for Firestore.

Package Exports

  • redux-firestore

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (redux-firestore) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

redux-firestore

NPM version NPM downloads License Code Style Dependency Status Build Status

Gitter

Redux bindings for Firestore. Provides low-level API used in other libraries such as react-redux-firebase

Installation

npm install redux-firestore --save

NOTE

You probably want to be using react-redux-firebase.

If you are planning on using Firestore with Auth, Realtime DB, Storage or any other Firebase Products you will most likely want to use react-redux-firebase, which actually depends on this module.

This is a low level library that is meant to be used simple as a building block.

Use

import { createStore, combineReducers, compose } from 'redux'
import { reduxFirestore, firestoreReducer } from 'redux-firestore'
import firebase from 'firebase'
import 'firebase/firestore'

const firebaseConfig = {} // from Firebase Console
const rfConfig = {} // redux-firestore config

// Initialize firebase instance
const firebaseApp = firebase.initializeApp(config)
firebase.firestore(); // Initialize Cloud Firestore through Firebase

// Add reduxReduxFirebase to compose
const createStoreWithFirebase = compose(
  reduxFirestore(firebaseApp, rfConfig), // firebase instance as first argument
)(createStore)

// Add Firebase to reducers
const rootReducer = combineReducers({
  firestore: firestoreReducer
})

// Create store with reducers and initial state
const initialState = {}
const store = createStoreWithFirebase(rootReducer, initialState)

Then pass store to your component's context using react-redux's Provider:

ReactDOM.render(
  <Provider store={store}>
    <MyRootComponent />
  </Provider>,
  rootEl
)

Call Firestore

Firestore Instance

Functional Components

It is common to make react components "stateless" meaning that the component is just a function. This can be useful, but then can limit usage of lifecycle hooks and other features of Component Classes. recompose helps solve this by providing Higher Order Component functions such as withContext, lifecycle, and withHandlers.

import { connect } from 'react-redux'
import {
  compose,
  withHandlers,
  lifecycle,
  withContext,
  getContext
} from 'recompose'

const withStore = compose(
  withContext({ store: PropTypes.object }, () => {}),
  getContext({ store: PropTypes.object }),
)

const enhance = compose(
  withStore,
  withHandlers({
    loadData: props => () => props.store.firestore.get('todos'),
    onDoneClick: props => (key, done = false) =>
      props.store.firestore.update('todos', key, { done }),
    onNewSubmit: props => newTodo =>
      props.store.firestore.add('todos', { ...newTodo, owner: 'Anonymous' }),
  }),
  lifecycle({
    componentWillMount(props) {
      props.loadData()
    }
  }),
  connect(({ firebase }) => ({ // state.firebase
    todos: firebase.ordered.todos,
  }))
)

export default enhance(SomeComponent)

For more information on using recompose visit the docs

Component Class
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { isEqual } from 'lodash'
import { watchEvents, unWatchEvents } from './actions/query'
import { getEventsFromInput, createCallable } from './utils'

class Todos extends Component {
  static contextTypes = {
    store: PropTypes.object.isRequired
  }

  componentWillMount () {
    const { firebase } = this.context.store
    firestore.get('todos')
  }

  render () {
    return (
      <div>
        {
          todos.map(todo => (
            <div key={todo.id}>
              {JSON.stringify(todo)}
            </div>
          ))
        }
      </div>
    )
  }
}

export default connect((state) => ({
  todos: state.firestore.ordered.todos
}))(Todos)

Types of Queries

get
props.store.firestore.get({ collection: 'cities' }),
// store.firestore.get({ collection: 'cities', doc: 'SF' }), // doc
onSnapshot/setListener
store.firestore.onSnapshot({ collection: 'cities' }),
// store.firestore.setListener({ collection: 'cities' }), // alias
// store.firestore.setListener({ collection: 'cities', doc: 'SF' }), // doc

setListeners

store.firestore.setListeners([
  { collection: 'cities' },
  { collection: 'users' },
]),

Query Options

Collection
{ collection: 'cities' },
// or string equivalent
// store.firestore.get('cities'),
Document
{ collection: 'cities', doc: 'SF' },
// or string equivalent
// props.store.firestore.get('cities/SF'),
Sub Collections
{ collection: 'cities', doc: 'SF', subcollections: [{ collection: 'zipcodes' }] },
// or string equivalent
// props.store.firestore.get('cities/SF'),
Where

Single To create a single where call, pass a single argument array to where

{
  collection: 'cities',
  where: ['state', '==', 'CA']
},

Multiple

Multiple where queries are as simple as passing multiple argument arrays (each one representing a where call)

{
  collection: 'cities',
  where: [
    ['state', '==', 'CA'],
    ['population', '<', 100000]
  ]
},

Should only be used with collections

storeAs

Storing data under a different path within redux is as easy as passing the storeAs parameter to your query:

{
  collection: 'cities',
  where: ['state', '==', 'CA'],
  storeAs: 'caliCities' // store data in redux under this path instead of "cities"
},

NOTE: Not yet supported for subcollections

Other Firebase Statics

Other Firebase statics (such as FieldValue) are available through the firestore instance:

import { connect } from 'react-redux'
import {
  compose,
  withHandlers,
  lifecycle,
  withContext,
  getContext
} from 'recompose'

const withFirestore = compose(
  withContext({ store: PropTypes.object }, () => {}),
  getContext({ store: PropTypes.object }),
)

const enhance = compose(
  withStore,
  withHandlers({
    onDoneClick: props => (key, done = true) => {
      const { firestore } = props.store
      return firestore.update('todos', key, {
        done,
        updatedAt: firestore.FieldValue.serverTimestamp() // use static from firestore instance
      }),
    }
  })
)

export default enhance(SomeComponent)

Applications Using This

Roadmap

v0.1.0 - Basic querying

redux-firestore can be used along side react-redux-firebase