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
Redux bindings for Firestore. Provides low-level API used in other libraries such as react-redux-firebase
Installation
npm install redux-firestore --saveThis assumes you are using npm as your package manager.
If you're not, you can access the library on unpkg, download it, or point your package manager to it.
Most commonly people consume Redux Firestore as a CommonJS module. This module is what you get when you import redux in a Webpack, Browserify, or a Node environment.
If you don't use a module bundler, it's also fine. The redux-firestore npm package includes precompiled production and development UMD builds in the dist folder. They can be used directly without a bundler and are thus compatible with many popular JavaScript module loaders and environments. For example, you can drop a UMD build as a <script> tag on the page. The UMD builds make Redux Firestore available as a window.ReduxFirestore global variable.
It can be imported like so:
<script src="../node_modules/redux-firestore/dist/redux-firestore.min.js"></script>
<!-- or through cdn: <script src="https://unpkg.com/redux-firestore@0.1.0/dist/redux-firestore.min.js"></script> -->
<script>console.log('redux firestore:', window.ReduxFirestore)</script>Note: In an effort to keep things simple, the wording from this explanation was modeled after the installation section of the Redux Docs.
Complementary Package
Most likely, you'll want react bindings, for that you will need: react-redux-firebase.
npm install --save react-redux-firebase@nextIf you are planning on using Firestore with Auth, Realtime DB, Storage or any other Firebase Products you will need to install react-redux-firebase, which actually works nicely alongside this module.
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
// Initialize firebase instance
const firebaseApp = firebase.initializeApp(config)
// Initialize Cloud Firestore through Firebase
firebase.firestore();
// Add reduxReduxFirebase to compose
const createStoreWithFirebase = compose(
reduxFirestore(firebaseApp), // 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' }), // doconSnapshot/setListener
store.firestore.onSnapshot({ collection: 'cities' }),
// store.firestore.setListener({ collection: 'cities' }), // alias
// store.firestore.setListener({ collection: 'cities', doc: 'SF' }), // docsetListeners
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
To create a single where call, pass a single argument array to the where parameter:
{
collection: 'cities',
where: ['state', '==', 'CA']
},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
orderBy
To create a single orderBy call, pass a single argument array to orderBy
{
collection: 'cities',
orderBy: ['state'],
// orderBy: 'state' // string notation can also be used
},Multiple orderBys are as simple as passing multiple argument arrays (each one representing a orderBy call)
{
collection: 'cities',
orderBy: [
['state'],
['population', 'desc']
]
},Should only be used with collections
limit
Limit the query to a certain number of results
{
collection: 'cities',
limit: 10
},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 inside of subcollections (only at the top level)
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
- fireadmin.io - Firebase Instance Management Tool source available here
Roadmap
- Support for Passing a Ref to
setListenerin place ofqueryConfig