1 | # redux-firestore
|
2 |
|
3 | [![NPM version][npm-image]][npm-url]
|
4 | [![NPM downloads][npm-downloads-image]][npm-url]
|
5 | [![License][license-image]][license-url]
|
6 | [![Code Style][code-style-image]][code-style-url]
|
7 | [![Dependency Status][daviddm-image]][daviddm-url]
|
8 | [![Build Status][travis-image]][travis-url]
|
9 | [![Code Coverage][coverage-image]][coverage-url]
|
10 |
|
11 | [![Gitter][gitter-image]][gitter-url]
|
12 |
|
13 |
|
14 |
|
15 | > Redux bindings for Firestore. Provides low-level API used in other libraries such as [react-redux-firebase](https://github.com/prescottprue/react-redux-firebase)
|
16 |
|
17 | ## Installation
|
18 |
|
19 | ```sh
|
20 | npm install redux-firestore --save
|
21 | ```
|
22 |
|
23 | This assumes you are using [npm](https://www.npmjs.com/) as your package manager.
|
24 |
|
25 | If you're not, you can access the library on [unpkg](https://unpkg.com/redux-firestore@latest/dist/redux-firestore.min.js), download it, or point your package manager to it. Theres more on this in the [Builds section below](#builds)
|
26 |
|
27 | ## Complementary Package
|
28 |
|
29 | Most likely, you'll want react bindings, for that you will need [react-redux-firebase](https://github.com/prescottprue/react-redux-firebase). You can install the current version it by running:
|
30 |
|
31 | ```sh
|
32 | npm install --save react-redux-firebase
|
33 | ```
|
34 |
|
35 | [react-redux-firebase](https://github.com/prescottprue/react-redux-firebase) provides [`withFirestore`](http://react-redux-firebase.com/docs/api/withFirestore.html) and [`firestoreConnect`](http://react-redux-firebase.com/docs/api/firestoreConnect.html) higher order components, which handle automatically calling `redux-firestore` internally based on component's lifecycle (i.e. mounting/un-mounting)
|
36 |
|
37 | ## Use
|
38 |
|
39 | ```javascript
|
40 | import { createStore, combineReducers, compose } from 'redux';
|
41 | import { reduxFirestore, firestoreReducer } from 'redux-firestore';
|
42 | import firebase from 'firebase/app';
|
43 | import 'firebase/auth';
|
44 | import 'firebase/database';
|
45 | import 'firebase/firestore';
|
46 |
|
47 | const firebaseConfig = {}; // from Firebase Console
|
48 | const rfConfig = {}; // optional redux-firestore Config Options
|
49 |
|
50 | // Initialize firebase instance
|
51 | firebase.initializeApp(firebaseConfig);
|
52 | // Initialize Cloud Firestore through Firebase
|
53 | firebase.firestore();
|
54 |
|
55 | // Add reduxFirestore store enhancer to store creator
|
56 | const createStoreWithFirebase = compose(
|
57 | reduxFirestore(firebase, rfConfig), // firebase instance as first argument, rfConfig as optional second
|
58 | )(createStore);
|
59 |
|
60 | // Add Firebase to reducers
|
61 | const rootReducer = combineReducers({
|
62 | firestore: firestoreReducer,
|
63 | });
|
64 |
|
65 | // Create store with reducers and initial state
|
66 | const initialState = {};
|
67 | const store = createStoreWithFirebase(rootReducer, initialState);
|
68 | ```
|
69 |
|
70 | Then pass store to your component's context using [react-redux's `Provider`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store):
|
71 |
|
72 | ```js
|
73 | import React from 'react';
|
74 | import { render } from 'react-dom';
|
75 | import { Provider } from 'react-redux';
|
76 |
|
77 | render(
|
78 | <Provider store={store}>
|
79 | <MyRootComponent />
|
80 | </Provider>,
|
81 | rootEl,
|
82 | );
|
83 | ```
|
84 |
|
85 | ### Call Firestore
|
86 |
|
87 | #### Firestore Instance
|
88 |
|
89 | ##### Functional Components
|
90 |
|
91 | It is common to make react components "functional" meaning that the component is just a function instead of being a `class` which `extends React.Component`. This can be useful, but can limit usage of lifecycle hooks and other features of Component Classes. [`recompose` helps solve this](https://github.com/acdlite/recompose/blob/master/docs/API.md) by providing Higher Order Component functions such as `withContext`, `lifecycle`, and `withHandlers`.
|
92 |
|
93 | ```js
|
94 | import { connect } from 'react-redux';
|
95 | import {
|
96 | compose,
|
97 | withHandlers,
|
98 | lifecycle,
|
99 | withContext,
|
100 | getContext,
|
101 | } from 'recompose';
|
102 |
|
103 | const withStore = compose(
|
104 | withContext({ store: PropTypes.object }, () => {}),
|
105 | getContext({ store: PropTypes.object }),
|
106 | );
|
107 |
|
108 | const enhance = compose(
|
109 | withStore,
|
110 | withHandlers({
|
111 | loadData: props => () => props.store.firestore.get('todos'),
|
112 | onDoneClick: props => (key, done = false) =>
|
113 | props.store.firestore.update(`todos/${key}`, { done }),
|
114 | onNewSubmit: props => newTodo =>
|
115 | props.store.firestore.add('todos', { ...newTodo, owner: 'Anonymous' }),
|
116 | }),
|
117 | lifecycle({
|
118 | componentDidMount(props) {
|
119 | props.loadData();
|
120 | },
|
121 | }),
|
122 | connect(({ firebase }) => ({
|
123 | // state.firebase
|
124 | todos: firebase.ordered.todos,
|
125 | })),
|
126 | );
|
127 |
|
128 | export default enhance(SomeComponent);
|
129 | ```
|
130 |
|
131 | For more information [on using recompose visit the docs](https://github.com/acdlite/recompose/blob/master/docs/API.md)
|
132 |
|
133 | ##### Component Class
|
134 |
|
135 | ```js
|
136 | import React, { Component } from 'react';
|
137 | import PropTypes from 'prop-types';
|
138 | import { connect } from 'react-redux';
|
139 | import { watchEvents, unWatchEvents } from './actions/query';
|
140 | import { getEventsFromInput, createCallable } from './utils';
|
141 |
|
142 | class Todos extends Component {
|
143 | static contextTypes = {
|
144 | store: PropTypes.object.isRequired,
|
145 | };
|
146 |
|
147 | componentDidMount() {
|
148 | const { firestore } = this.context.store;
|
149 | firestore.get('todos');
|
150 | }
|
151 |
|
152 | render() {
|
153 | return (
|
154 | <div>
|
155 | {todos.map(todo => (
|
156 | <div key={todo.id}>{JSON.stringify(todo)}</div>
|
157 | ))}
|
158 | </div>
|
159 | );
|
160 | }
|
161 | }
|
162 |
|
163 | export default connect(state => ({
|
164 | todos: state.firestore.ordered.todos,
|
165 | }))(Todos);
|
166 | ```
|
167 |
|
168 | ### API
|
169 |
|
170 | The `store.firestore` instance created by the `reduxFirestore` enhancer extends [Firebase's JS API for Firestore](https://firebase.google.com/docs/reference/js/firebase.firestore). This means all of the methods regularly available through `firebase.firestore()` and the statics available from `firebase.firestore` are available. Certain methods (such as `get`, `set`, and `onSnapshot`) have a different API since they have been extended with action dispatching. The methods which have dispatch actions are listed below:
|
171 |
|
172 | #### Actions
|
173 |
|
174 | ##### get
|
175 |
|
176 | ```js
|
177 | store.firestore.get({ collection: 'cities' }),
|
178 | // store.firestore.get({ collection: 'cities', doc: 'SF' }), // doc
|
179 | ```
|
180 |
|
181 | ##### set
|
182 |
|
183 | ```js
|
184 | store.firestore.set({ collection: 'cities', doc: 'SF' }, { name: 'San Francisco' }),
|
185 | ```
|
186 |
|
187 | ##### add
|
188 |
|
189 | ```js
|
190 | store.firestore.add({ collection: 'cities' }, { name: 'Some Place' }),
|
191 | ```
|
192 |
|
193 | ##### update
|
194 |
|
195 | ```js
|
196 | const itemUpdates = {
|
197 | some: 'value',
|
198 | updatedAt: store.firestore.FieldValue.serverTimestamp()
|
199 | }
|
200 |
|
201 | store.firestore.update({ collection: 'cities', doc: 'SF' }, itemUpdates),
|
202 | ```
|
203 |
|
204 | ##### delete
|
205 |
|
206 | ```js
|
207 | store.firestore.delete({ collection: 'cities', doc: 'SF' }),
|
208 | ```
|
209 |
|
210 | ##### runTransaction
|
211 |
|
212 | ```js
|
213 | store.firestore
|
214 | .runTransaction(t => {
|
215 | return t.get(cityRef).then(doc => {
|
216 | // Add one person to the city population
|
217 | const newPopulation = doc.data().population + 1;
|
218 | t.update(cityRef, { population: newPopulation });
|
219 | });
|
220 | })
|
221 | .then(result => {
|
222 | // TRANSACTION_SUCCESS action dispatched
|
223 | console.log('Transaction success!');
|
224 | })
|
225 | .catch(err => {
|
226 | // TRANSACTION_FAILURE action dispatched
|
227 | console.log('Transaction failure:', err);
|
228 | });
|
229 | ```
|
230 |
|
231 | #### Types of Queries
|
232 |
|
233 | Each of these functions take a queryOptions object with options as described in the [Query Options section of this README](#query-options). Some simple query options examples are used here for better comprehension.
|
234 |
|
235 | ##### get
|
236 |
|
237 | ```js
|
238 | props.store.firestore.get({ collection: 'cities' }),
|
239 | // store.firestore.get({ collection: 'cities', doc: 'SF' }), // doc
|
240 | ```
|
241 |
|
242 | ##### onSnapshot/setListener
|
243 |
|
244 | ```js
|
245 | store.firestore.onSnapshot({ collection: 'cities' }),
|
246 | // store.firestore.setListener({ collection: 'cities' }), // alias
|
247 | // store.firestore.setListener({ collection: 'cities', doc: 'SF' }), // doc
|
248 | ```
|
249 |
|
250 | ##### setListeners
|
251 |
|
252 | ```js
|
253 | store.firestore.setListeners([
|
254 | { collection: 'cities' },
|
255 | { collection: 'users' },
|
256 | ]),
|
257 | ```
|
258 |
|
259 | ##### unsetListener / unsetListeners
|
260 |
|
261 | After setting a listener/multiple listeners, you can unset them with the following two functions. In order to unset a specific listener, you must pass the same queryOptions object given to onSnapshot/setListener(s).
|
262 |
|
263 | ```js
|
264 | store.firestore.unsetListener({ collection: 'cities' }),
|
265 | // of for any number of listeners at once :
|
266 | store.firestore.unsetListeners([query1Options, query2Options]),
|
267 | // here query1Options as in { collection: 'cities' } for example
|
268 | ```
|
269 |
|
270 | #### Query Options
|
271 |
|
272 | ##### Collection
|
273 |
|
274 | ```js
|
275 | { collection: 'cities' },
|
276 | // or string equivalent
|
277 | // store.firestore.get('cities'),
|
278 | ```
|
279 |
|
280 | ##### Document
|
281 |
|
282 | ```js
|
283 | { collection: 'cities', doc: 'SF' },
|
284 | // or string equivalent
|
285 | // props.store.firestore.get('cities/SF'),
|
286 | ```
|
287 |
|
288 | ##### Sub Collections
|
289 |
|
290 | ```js
|
291 | {
|
292 | collection: 'cities',
|
293 | doc: 'SF',
|
294 | subcollections: [{ collection: 'zipcodes' }],
|
295 | storeAs: 'SF-zipcodes' // make sure to include this
|
296 | },
|
297 | ```
|
298 |
|
299 | **NOTE**: `storeAs` is now required for subcollections. This is to more closely match the logic of [the upcoming major release (v1)](https://github.com/prescottprue/redux-firestore/wiki/v1.0.0-Roadmap) which stores all collections, even subcollections, at the top level of `data` and `ordered` state slices.
|
300 |
|
301 | ##### Collection Group
|
302 |
|
303 | ```js
|
304 | { collectionGroup: 'landmarks' },
|
305 | // does not support string equivalent
|
306 | ```
|
307 |
|
308 | **Note:** When nesting sub-collections, [`storeAs`](#storeas) should be used for more optimal state updates.
|
309 |
|
310 | ##### Where
|
311 |
|
312 | To create a single `where` call, pass a single argument array to the `where` parameter:
|
313 |
|
314 | ```js
|
315 | {
|
316 | collection: 'cities',
|
317 | where: ['state', '==', 'CA']
|
318 | },
|
319 | ```
|
320 |
|
321 | Multiple `where` queries are as simple as passing multiple argument arrays (each one representing a `where` call):
|
322 |
|
323 | ```js
|
324 | {
|
325 | collection: 'cities',
|
326 | where: [
|
327 | ['state', '==', 'CA'],
|
328 | ['population', '<', 100000]
|
329 | ]
|
330 | },
|
331 | ```
|
332 |
|
333 | Firestore doesn't allow you to create `or` style queries. Instead, you should pass in multiple queries and compose your data.
|
334 |
|
335 | ```javascript
|
336 | ['sally', 'john', 'peter'].map(friendId => ({
|
337 | collection: 'users',
|
338 | where: [
|
339 | ['id', '==', friendId],
|
340 | ['isOnline', '==', true]
|
341 | ]
|
342 | storeAs: 'onlineFriends'
|
343 | }));
|
344 | ```
|
345 |
|
346 | Since the results must be composed, a query like this is unable to be properly ordered. The results should be pulled from `data`.
|
347 |
|
348 | _Can only be used with collections_
|
349 |
|
350 | ##### orderBy
|
351 |
|
352 | To create a single `orderBy` call, pass a single argument array to `orderBy`
|
353 |
|
354 | ```js
|
355 | {
|
356 | collection: 'cities',
|
357 | orderBy: ['state'],
|
358 | // orderBy: 'state' // string notation can also be used
|
359 | },
|
360 | ```
|
361 |
|
362 | Multiple `orderBy`s are as simple as passing multiple argument arrays (each one representing a `orderBy` call)
|
363 |
|
364 | ```js
|
365 | {
|
366 | collection: 'cities',
|
367 | orderBy: [
|
368 | ['state'],
|
369 | ['population', 'desc']
|
370 | ]
|
371 | },
|
372 | ```
|
373 |
|
374 | _Can only be used with collections_
|
375 |
|
376 | ##### limit
|
377 |
|
378 | Limit the query to a certain number of results
|
379 |
|
380 | ```js
|
381 | {
|
382 | collection: 'cities',
|
383 | limit: 10
|
384 | },
|
385 | ```
|
386 |
|
387 | _Can only be used with collections_
|
388 |
|
389 | ##### startAt
|
390 |
|
391 | > Creates a new query where the results start at the provided document (inclusive)
|
392 |
|
393 | [From Firebase's `startAt` docs](https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference#startAt)
|
394 |
|
395 | ```js
|
396 | {
|
397 | collection: 'cities',
|
398 | orderBy: 'population',
|
399 | startAt: 1000000
|
400 | },
|
401 | ```
|
402 |
|
403 | _Can only be used with collections. Types can be a string, number, Date object, or an array of these types, but not a Firestore Document Snapshot_
|
404 |
|
405 | ##### startAfter
|
406 |
|
407 | > Creates a new query where the results start after the provided document (exclusive)...
|
408 |
|
409 | [From Firebase's `startAfter` docs](https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference#startAfter)
|
410 |
|
411 | ```js
|
412 | {
|
413 | collection: 'cities',
|
414 | orderBy: [['state', 'asc'],['population','desc']]
|
415 | startAfter: ["CA", 1000000]
|
416 | },
|
417 | ```
|
418 |
|
419 | **Note:** for the above to return valid results, there must be at least one document with `state = "CA"` _and_ `population = 1000000` (i.e. the values idenify "the provided document").
|
420 |
|
421 | _Can only be used with collections. Types can be a string, number, Date object, or an array of these types, but not a Firestore Document Snapshot_
|
422 |
|
423 | ##### endAt
|
424 |
|
425 | > Creates a new query where the results end at the provided document (inclusive)...
|
426 |
|
427 | [From Firebase's `endAt` docs](https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference#endAt)
|
428 |
|
429 | ```js
|
430 | {
|
431 | collection: 'cities',
|
432 | orderBy: 'population',
|
433 | endAt: 1000000
|
434 | },
|
435 | ```
|
436 |
|
437 | _Can only be used with collections. Types can be a string, number, Date object, or an array of these types, but not a Firestore Document Snapshot_
|
438 |
|
439 | ##### endBefore
|
440 |
|
441 | > Creates a new query where the results end before the provided document (exclusive) ...
|
442 |
|
443 | [From Firebase's `endBefore` docs](https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference#endBefore)
|
444 |
|
445 | ```js
|
446 | {
|
447 | collection: 'cities',
|
448 | orderBy: 'population',
|
449 | endBefore: 1000000
|
450 | },
|
451 | ```
|
452 |
|
453 | _Can only be used with collections. Types can be a string, number, Date object, or an array of these types, but not a Firestore Document Snapshot_
|
454 |
|
455 | ##### storeAs
|
456 |
|
457 | Storing data under a different path within redux is as easy as passing the `storeAs` parameter to your query:
|
458 |
|
459 | ```js
|
460 | {
|
461 | collection: 'cities',
|
462 | where: ['state', '==', 'CA'],
|
463 | storeAs: 'caliCities' // store data in redux under this path instead of "cities"
|
464 | },
|
465 | ```
|
466 |
|
467 | **Note:** Usage of `"/"` and `"."` within `storeAs` can cause unexpected behavior when attempting to retrieve from redux state
|
468 |
|
469 | #### Other Firebase Statics
|
470 |
|
471 | Other Firebase statics (such as [FieldValue](https://firebase.google.com/docs/reference/js/firebase.firestore.FieldValue)) are available through the firestore instance:
|
472 |
|
473 | ```js
|
474 | import PropTypes from 'prop-types'
|
475 | import { connect } from 'react-redux'
|
476 | import {
|
477 | compose,
|
478 | withHandlers,
|
479 | withContext,
|
480 | getContext
|
481 | } from 'recompose'
|
482 |
|
483 | const withStore = compose(
|
484 | withContext({ store: PropTypes.object }, () => {}),
|
485 | getContext({ store: PropTypes.object }),
|
486 | )
|
487 |
|
488 | const enhance = compose(
|
489 | withStore,
|
490 | withHandlers({
|
491 | onDoneClick: props => (key, done = true) => {
|
492 | const { firestore } = props.store
|
493 | return firestore.update(`todos/${key}`, {
|
494 | done,
|
495 | updatedAt: firestore.FieldValue.serverTimestamp() // use static from firestore instance
|
496 | }),
|
497 | }
|
498 | })
|
499 | )
|
500 |
|
501 | export default enhance(SomeComponent)
|
502 | ```
|
503 |
|
504 | ### Population
|
505 |
|
506 | Population, made popular in [react-redux-firebase](http://react-redux-firebase.com/docs/recipes/populate.html), also works with firestore.
|
507 |
|
508 | #### Automatic Listeners
|
509 |
|
510 | ```js
|
511 | import { connect } from 'react-redux';
|
512 | import { firestoreConnect, populate } from 'react-redux-firebase';
|
513 | import {
|
514 | compose,
|
515 | withHandlers,
|
516 | lifecycle,
|
517 | withContext,
|
518 | getContext,
|
519 | } from 'recompose';
|
520 |
|
521 | const populates = [{ child: 'createdBy', root: 'users' }];
|
522 | const collection = 'projects';
|
523 |
|
524 | const withPopulatedProjects = compose(
|
525 | firestoreConnect(props => [
|
526 | {
|
527 | collection,
|
528 | populates,
|
529 | },
|
530 | ]),
|
531 | connect((state, props) => ({
|
532 | projects: populate(state.firestore, collection, populates),
|
533 | })),
|
534 | );
|
535 | ```
|
536 |
|
537 | #### Manually using setListeners
|
538 |
|
539 | ```js
|
540 | import { withFirestore, populate } from 'react-redux-firebase';
|
541 | import { connect } from 'react-redux';
|
542 | import { compose, lifecycle } from 'recompose';
|
543 |
|
544 | const collection = 'projects';
|
545 | const populates = [{ child: 'createdBy', root: 'users' }];
|
546 |
|
547 | const enhance = compose(
|
548 | withFirestore,
|
549 | lifecycle({
|
550 | componentDidMount() {
|
551 | this.props.firestore.setListener({ collection, populates });
|
552 | },
|
553 | }),
|
554 | connect(({ firestore }) => ({
|
555 | // state.firestore
|
556 | todos: firestore.ordered.todos,
|
557 | })),
|
558 | );
|
559 | ```
|
560 |
|
561 | #### Manually using get
|
562 |
|
563 | ```js
|
564 | import { withFirestore, populate } from 'react-redux-firebase';
|
565 | import { connect } from 'react-redux';
|
566 | import { compose, lifecycle } from 'recompose';
|
567 |
|
568 | const collection = 'projects';
|
569 | const populates = [{ child: 'createdBy', root: 'users' }];
|
570 |
|
571 | const enhance = compose(
|
572 | withFirestore,
|
573 | lifecycle({
|
574 | componentDidMount() {
|
575 | this.props.store.firestore.get({ collection, populates });
|
576 | },
|
577 | }),
|
578 | connect(({ firestore }) => ({
|
579 | // state.firestore
|
580 | todos: firestore.ordered.todos,
|
581 | })),
|
582 | );
|
583 | ```
|
584 |
|
585 | ## Config Options
|
586 |
|
587 | Optional configuration options for redux-firestore, provided to reduxFirestore enhancer as optional second argument. Combine any of them together in an object.
|
588 |
|
589 | #### logListenerError
|
590 |
|
591 | Default: `true`
|
592 |
|
593 | Whether or not to use `console.error` to log listener error objects. Errors from listeners are helpful to developers on multiple occasions including when index needs to be added.
|
594 |
|
595 | #### enhancerNamespace
|
596 |
|
597 | Default: `'firestore'`
|
598 |
|
599 | Namespace under which enhancer places internal instance on redux store (i.e. `store.firestore`).
|
600 |
|
601 | #### allowMultipleListeners
|
602 |
|
603 | Default: `false`
|
604 |
|
605 | Whether or not to allow multiple listeners to be attached for the same query. If a function is passed the arguments it receives are `listenerToAttach`, `currentListeners`, and the function should return a boolean.
|
606 |
|
607 | #### preserveOnDelete
|
608 |
|
609 | Default: `null`
|
610 |
|
611 | Values to preserve from state when DELETE_SUCCESS action is dispatched. Note that this will not prevent the LISTENER_RESPONSE action from removing items from state.ordered if you have a listener attached.
|
612 |
|
613 | #### preserveOnListenerError
|
614 |
|
615 | Default: `null`
|
616 |
|
617 | Values to preserve from state when LISTENER_ERROR action is dispatched.
|
618 |
|
619 | #### onAttemptCollectionDelete
|
620 |
|
621 | Default: `null`
|
622 |
|
623 | Arguments:`(queryOption, dispatch, firebase)`
|
624 |
|
625 | Function run when attempting to delete a collection. If not provided (default) delete promise will be rejected with "Only documents can be deleted" unless. This is due to the fact that Collections can not be deleted from a client, it should instead be handled within a cloud function (which can be called by providing a promise to `onAttemptCollectionDelete` that calls the cloud function).
|
626 |
|
627 | #### mergeOrdered
|
628 |
|
629 | Default: `true`
|
630 |
|
631 | Whether or not to merge data within `orderedReducer`.
|
632 |
|
633 | #### mergeOrderedDocUpdate
|
634 |
|
635 | Default: `true`
|
636 |
|
637 | Whether or not to merge data from document listener updates within `orderedReducer`.
|
638 |
|
639 | #### mergeOrderedCollectionUpdates
|
640 |
|
641 | Default: `true`
|
642 |
|
643 | Whether or not to merge data from collection listener updates within `orderedReducer`.
|
644 |
|
645 |
|
646 |
|
647 | `redux-firestore`'s enhancer offers a new middleware setup that was not offered in `react-redux-firebase` (but will eventually make it `redux-firebase`)
|
648 | **Note**: This syntax is just a sample and is not currently released
|
649 |
|
650 | ##### Setup
|
651 | ```js
|
652 | ```
|
653 |
|
654 |
|
655 | ##### Usage
|
656 |
|
657 | ```js
|
658 | import { FIREBASE_CALL } from 'redux-firestore'
|
659 |
|
660 | dispatch({
|
661 | type: FIREBASE_CALL,
|
662 | collection: 'users', // only used when namespace is firestore
|
663 | method: 'get' // get method
|
664 | })
|
665 | ```
|
666 |
|
667 | Some of the goals behind this approach include:
|
668 |
|
669 | 1. Not needing to pass around a Firebase instance (with `react-redux-firebase` this meant using `firebaseConnect` HOC or `getFirebase`)
|
670 | 2. Follows [patterns outlined in the redux docs for data fetching](http://redux.js.org/docs/advanced/ExampleRedditAPI.html)
|
671 | 3. Easier to expand/change internal API as Firebase/Firestore API grows & changes -->
|
672 |
|
673 | ## Builds
|
674 |
|
675 | Most commonly people consume Redux Firestore as a [CommonJS module](http://webpack.github.io/docs/commonjs.html). This module is what you get when you import redux in a Webpack, Browserify, or a Node environment.
|
676 |
|
677 | If you don't use a module bundler, it's also fine. The redux-firestore npm package includes precompiled production and development [UMD builds](https://github.com/umdjs/umd) in the [dist folder](https://unpkg.com/redux-firestore@latest/dist/). 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.
|
678 |
|
679 | It can be imported like so:
|
680 |
|
681 | ```html
|
682 | <script src="../node_modules/redux-firestore/dist/redux-firestore.min.js"></script>
|
683 | <!-- or through cdn: <script src="https://unpkg.com/redux-firestore@latest/dist/redux-firestore.min.js"></script> -->
|
684 | <script>
|
685 | console.log('redux firestore:', window.ReduxFirestore);
|
686 | </script>
|
687 | ```
|
688 |
|
689 | **Note:** In an effort to keep things simple, the wording from this explanation was modeled after [the installation section of the Redux Docs](https://redux.js.org/#installation).
|
690 |
|
691 | ## Applications Using This
|
692 |
|
693 | - [fireadmin.io](http://fireadmin.io) - Firebase Instance Management Tool (source [available here](https://github.com/prescottprue/fireadmin))
|
694 |
|
695 | ## FAQ
|
696 |
|
697 | 1. How do I update a document within a subcollection?
|
698 |
|
699 | Provide `subcollections` config the same way you do while querying:
|
700 |
|
701 | ```js
|
702 | props.firestore.update(
|
703 | {
|
704 | collection: 'cities',
|
705 | doc: 'SF',
|
706 | subcollections: [{ collection: 'counties', doc: 'San Mateo' }],
|
707 | },
|
708 | { some: 'changes' },
|
709 | );
|
710 | ```
|
711 |
|
712 | 1. How do I get auth state in redux?
|
713 |
|
714 | You will most likely want to use [`react-redux-firebase`](https://github.com/prescottprue/react-redux-firebase) or another redux/firebase connector. For more information please visit the [complementary package section](#complementary-package).
|
715 |
|
716 | 1. Are there Higher Order Components for use with React?
|
717 |
|
718 | [`react-redux-firebase`](https://github.com/prescottprue/react-redux-firebase) contains `firebaseConnect`, `firestoreConnect`, `withFirebase` and `withFirestore` HOCs. For more information please visit the [complementary package section](#complementary-package).
|
719 |
|
720 | ## Roadmap
|
721 |
|
722 | - Automatic support for documents that have a parameter and a subcollection with the same name (currently requires `storeAs`)
|
723 | - Support for Passing a Ref to `setListener` in place of `queryConfig` object or string
|
724 |
|
725 | Post an issue with a feature suggestion if you have any ideas!
|
726 |
|
727 | [npm-image]: https://img.shields.io/npm/v/redux-firestore.svg?style=flat-square
|
728 | [npm-url]: https://npmjs.org/package/redux-firestore
|
729 | [npm-downloads-image]: https://img.shields.io/npm/dm/redux-firestore.svg?style=flat-square
|
730 | [quality-image]: http://npm.packagequality.com/shield/redux-firestore.svg?style=flat-square
|
731 | [quality-url]: https://packagequality.com/#?package=redux-firestore
|
732 | [travis-image]: https://img.shields.io/travis/prescottprue/redux-firestore/master.svg?style=flat-square
|
733 | [travis-url]: https://travis-ci.org/prescottprue/redux-firestore
|
734 | [daviddm-image]: https://img.shields.io/david/prescottprue/redux-firestore.svg?style=flat-square
|
735 | [daviddm-url]: https://david-dm.org/prescottprue/redux-firestore
|
736 | [climate-image]: https://img.shields.io/codeclimate/github/prescottprue/redux-firestore.svg?style=flat-square
|
737 | [climate-url]: https://codeclimate.com/github/prescottprue/redux-firestore
|
738 | [coverage-image]: https://img.shields.io/codecov/c/github/prescottprue/redux-firestore.svg?style=flat-square
|
739 | [coverage-url]: https://codecov.io/gh/prescottprue/redux-firestore
|
740 | [license-image]: https://img.shields.io/npm/l/redux-firestore.svg?style=flat-square
|
741 | [license-url]: https://github.com/prescottprue/redux-firestore/blob/master/LICENSE
|
742 | [code-style-image]: https://img.shields.io/badge/code%20style-airbnb-blue.svg?style=flat-square
|
743 | [code-style-url]: https://github.com/airbnb/javascript
|
744 | [gitter-image]: https://img.shields.io/gitter/room/redux-firestore/gitter.svg?style=flat-square
|
745 | [gitter-url]: https://gitter.im/redux-firestore/Lobby
|
746 |
|
\ | No newline at end of file |