1 | [![Build Status][build-badge]][build]
|
2 | [![codecov][coverage-badge]][coverage]
|
3 | [![MIT License][license-badge]][license]
|
4 | [![version][version-badge]][package]
|
5 |
|
6 | [![size][size-badge]][unpkg-dist]
|
7 | [![gzip size][gzip-badge]][unpkg-dist]
|
8 | [![module formats: umd, cjs, and es][module-formats-badge]][unpkg-dist]
|
9 |
|
10 | # react-firestore 🔥🏪
|
11 |
|
12 | React components to fetch collections and documents from Firestore
|
13 |
|
14 | ## The problem
|
15 |
|
16 | You want to use the new Firestore database from Google, but don't want to
|
17 | have to use redux or any other state management tool. You would like to not have
|
18 | to worry too much about the exact API for firestore (snapshots, references, etc),
|
19 | and just be able to retrieve collections and documents and read their data.
|
20 |
|
21 | You also want to do all this using [render props, because they're awesome](https://www.youtube.com/watch?v=BcVAq3YFiuc).
|
22 |
|
23 | ## The solution
|
24 |
|
25 | This is a set of components that allows you to interact with Firestore collections
|
26 | and documents, without needing to constantly call additional methods (like `.data()`)
|
27 | to display your data.
|
28 |
|
29 | There is still an escape hatch where the snapshot from Firestore is provided to
|
30 | your render function, in the event that you need more control over your interactions
|
31 | with Firestore.
|
32 |
|
33 | ## Disclaimer
|
34 |
|
35 | This project is still a work in progress and in an alpha state.
|
36 | The API may update frequently.
|
37 |
|
38 | ## Table of Contents
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | * [Installation](#installation)
|
45 | * [Usage](#usage)
|
46 | * [`FirestoreProvider`](#firestoreprovider)
|
47 | * [`FirestoreCollection`](#firestorecollection)
|
48 | * [`FirestoreDocument`](#firestoredocument)
|
49 | * [`Firestore`](#firestore)
|
50 | * [`withFirestore`](#withfirestore)
|
51 |
|
52 |
|
53 |
|
54 | ## Installation
|
55 |
|
56 | This package is available on [npm][npm].
|
57 |
|
58 | ```
|
59 | npm install --save react-firestore
|
60 | ```
|
61 |
|
62 | Or, if you're using [yarn][yarn]:
|
63 |
|
64 | ```
|
65 | yarn add react-firestore
|
66 | ```
|
67 |
|
68 | ## Usage
|
69 |
|
70 | There are 3 components provided with this package:
|
71 |
|
72 | * [FirestoreProvider](#firestoreprovider)
|
73 | * [FirestoreCollection](#firestorecollection)
|
74 | * [FirestoreDocument](#firestoredocument)
|
75 |
|
76 | ### `FirestoreProvider`
|
77 |
|
78 | This component allows the `FirestoreCollection` and `FirestoreDocument`
|
79 | components to communicate with Firestore.
|
80 |
|
81 | At the top level of your app, configure `firebase` and render the
|
82 | `FirestoreProvider` component.
|
83 |
|
84 | If you're using [create-react-app][create-react-app], your `index.js`
|
85 | file would look something like this:
|
86 |
|
87 |
|
88 | ```jsx
|
89 | import React from 'react';
|
90 | import ReactDOM from 'react-dom';
|
91 | import firebase from '@firebase/app';
|
92 | import '@firebase/firestore';
|
93 | import { FirestoreProvider } from 'react-firestore';
|
94 |
|
95 | import App from './App';
|
96 |
|
97 | const config = {
|
98 | apiKey: '<your_api_key>',
|
99 | projectId: '<your_firebase_project_id>',
|
100 | };
|
101 |
|
102 | firebase.initializeApp(config);
|
103 |
|
104 | ReactDOM.render(
|
105 | <FirestoreProvider firebase={firebase}>
|
106 | <App />
|
107 | </FirestoreProvider>,
|
108 | document.getElementById('root'),
|
109 | );
|
110 | ```
|
111 |
|
112 | _Important: Starting with Firebase v5.5.0 timestamp objects stored in Firestore get returned as Firebase
|
113 | Timestamp objects instead of regular Date() objects. To make your app compatible with this
|
114 | change, add the `useTimestampsInSnapshots` to the FirestoreProvider element. If you dont do this
|
115 | your app might break. For more information visit [the Firebase refrence documentation][https://firebase.google.com/docs/reference/js/firebase.firestore.Timestamp]._
|
116 |
|
117 | _Note: The reason for the separate imports for `@firebase/app` and `@firebase/firestore`
|
118 | is because `firestore` is not included in the default `firebase` wrapper package. See
|
119 | the [firestore package](https://www.npmjs.com/package/@firebase/firestore) for more details._
|
120 |
|
121 | #### `FirestoreProvider` props
|
122 |
|
123 | ##### firebase
|
124 |
|
125 | > `firebase` | _required_
|
126 |
|
127 | An already initialized `firebase` object from the [@firebase/app package](https://www.npmjs.com/package/@firebase/app).
|
128 |
|
129 | ### `FirestoreCollection`
|
130 |
|
131 | This component allows you to interact with a Firestore collection.
|
132 | Using this component, you can access the collection at a given `path`
|
133 | and provide sort options, perform queries, and paginate data.
|
134 |
|
135 | This component will setup a listener and update
|
136 | whenever the given collection is updated in Firestore.
|
137 |
|
138 | Example usage to get a collection and sort by some fields:
|
139 |
|
140 | ```jsx
|
141 | <FirestoreCollection
|
142 | path="stories"
|
143 | sort="publishedDate:desc,authorName"
|
144 | render={({ isLoading, data }) => {
|
145 | return isLoading ? (
|
146 | <Loading />
|
147 | ) : (
|
148 | <div>
|
149 | <h1>Stories</h1>
|
150 | <ul>
|
151 | {data.map(story => (
|
152 | <li key={story.id}>
|
153 | {story.title} - {story.authorName}
|
154 | </li>
|
155 | ))}
|
156 | </ul>
|
157 | </div>
|
158 | );
|
159 | }}
|
160 | />
|
161 | ```
|
162 |
|
163 | #### `FirestoreCollection` props
|
164 |
|
165 | ##### path
|
166 |
|
167 | > `string` | _required_
|
168 |
|
169 | The `/` separated path to the Firestore collection. Collections must
|
170 | contain an odd number of path segments.
|
171 |
|
172 | ##### sort
|
173 |
|
174 | > `string` | defaults to `null`
|
175 |
|
176 | A comma-delimited list of fields by which the query should be ordered.
|
177 | Each item in the list can be of the format `fieldName` or `fieldName:sortOrder`.
|
178 | The `sortOrder` piece can be either `asc` or `desc`. If just a field name is given,
|
179 | `sortOrder` defaults to `asc`.
|
180 |
|
181 | ##### limit
|
182 |
|
183 | > `number` | defaults to `null`
|
184 |
|
185 | The maximum number of documents to retrieve from the collection.
|
186 |
|
187 | ##### filter
|
188 |
|
189 | > `array` or `array of array` | defaults to `null`
|
190 |
|
191 | Passing in an array of strings creates a simple query to filter the collection by
|
192 |
|
193 | ```jsx
|
194 | <FirestoreCollection
|
195 | path={'users'}
|
196 | filter={['firstName', '==', 'Mike']}
|
197 | render={() => {
|
198 | /* render stuff*/
|
199 | }}
|
200 | />
|
201 | ```
|
202 |
|
203 | Passing in an array of arrays creates a compound query to filter the collection by
|
204 |
|
205 | ```jsx
|
206 | <FirestoreCollection
|
207 | path={'users'}
|
208 | filter={[['firstName', '==', 'Mike'], ['lastName', '==', 'Smith']]}
|
209 | render={() => {
|
210 | /* render stuff*/
|
211 | }}
|
212 | />
|
213 | ```
|
214 |
|
215 | Passing in document references allows you to filter by reference fields:
|
216 |
|
217 | ```jsx
|
218 | <FirestoreCollection
|
219 | path={'users'}
|
220 | filter={[
|
221 | 'organization',
|
222 | '==',
|
223 | firestore.collection('organizations').doc('foocorp'),
|
224 | ]}
|
225 | render={() => {
|
226 | /* render stuff*/
|
227 | }}
|
228 | />
|
229 | ```
|
230 |
|
231 | ##### render
|
232 |
|
233 | > function({}) | _required_
|
234 |
|
235 | This is the function where you render whatever you want based on the state of
|
236 | the `FirebaseCollection` component. The object provided to the `render` function
|
237 | contains the following fields:
|
238 |
|
239 | | property | type | description |
|
240 | | --------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
241 | | isLoading | `boolean` | Loading status for the firebase query. `true` until an initial payload from Firestore is received. |
|
242 | | error | `Error` | Error received from firebase when the listen fails or is cancelled. |
|
243 | | data | `Array<any>` | An array containing all of the documents in the collection. Each item will contain an `id` along with the other data contained in the document. |
|
244 | | snapshot | `QuerySnapshot` / `null` | The firestore `QuerySnapshot` created to get data for the collection. See [QuerySnapshot docs](https://cloud.google.com/nodejs/docs/reference/firestore/latest/QuerySnapshot) for more information. |
|
245 |
|
246 | ### `FirestoreDocument`
|
247 |
|
248 | This component allows you to retrieve a Firestore document from the given `path`.
|
249 |
|
250 | This component will setup a listener and update
|
251 | whenever the given document is updated in Firestore.
|
252 |
|
253 | ```jsx
|
254 | <FirestoreDocument
|
255 | path="stories/1"
|
256 | render={({ isLoading, data }) => {
|
257 | return isLoading ? (
|
258 | <Loading />
|
259 | ) : (
|
260 | <div>
|
261 | <h1>{data.title}</h1>
|
262 | <h2>
|
263 | {data.authorName} - {data.publishedDate}
|
264 | </h2>
|
265 | <p>{data.description}</p>
|
266 | </div>
|
267 | );
|
268 | }}
|
269 | />
|
270 | ```
|
271 |
|
272 | #### `FirestoreDocument` props
|
273 |
|
274 | ##### path
|
275 |
|
276 | > `string` | _required_
|
277 |
|
278 | The `/` separated path to the Firestore document.
|
279 |
|
280 | ##### render
|
281 |
|
282 | > function({}) | _required_
|
283 |
|
284 | This is the function where you render whatever you want based on the state of
|
285 | the `FirebaseDocument` component. The object provided to the `render` function
|
286 | contains the following fields:
|
287 |
|
288 | | property | type | description |
|
289 | | --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
290 | | isLoading | `boolean` | Loading status for the firebase query. `true` until an initial payload from Firestore is received. |
|
291 | | error | `Error` | Error received from firebase when parsing the document data. |
|
292 | | data | `Object` / `null` | The document that resides at the given `path`. Will be `null` until an initial payload is received. The document will contain an `id` along with the other data contained in the document. |
|
293 | | snapshot | `DocumentSnapshot` / `null` | The firestore `DocumentSnapshot` created to get data for the document. See [DocumentSnapshot docs](https://cloud.google.com/nodejs/docs/reference/firestore/latest/DocumentSnapshot) for more information. |
|
294 |
|
295 | ### `Firestore`
|
296 |
|
297 | This component supplies the firestore database to the function specified
|
298 | by the `render` prop. This component can be used if you need more flexibility
|
299 | than the `FirestoreCollection` and `FirestoreDocument` components provide,
|
300 | or if you would just rather interact directly with the `firestore` object.
|
301 |
|
302 | ```jsx
|
303 | <Firestore
|
304 | render={({ firestore }) => {
|
305 | // Do stuff with `firestore`
|
306 | return <div> /* Component markup */ </div>;
|
307 | }}
|
308 | />
|
309 | ```
|
310 |
|
311 | #### `Firestore` props
|
312 |
|
313 | ##### render
|
314 |
|
315 | > function({}) | _required_
|
316 |
|
317 | This is the function where you render whatever you want using the firestore
|
318 | object passed in.
|
319 |
|
320 | | property | type | description |
|
321 | | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
322 | | firestore | `Object` | The `Firestore` class from [firestore][firestore-package]. See the docs for the [Firestore class][firestore-class-docs] for more information. |
|
323 |
|
324 | ### `withFirestore`
|
325 |
|
326 | This higher-order component can be used to provide the firestore database
|
327 | directly to the wrapped component via the `firestore` prop.
|
328 |
|
329 | ```jsx
|
330 | class MyComponent extends Component {
|
331 | state = {
|
332 | story: null,
|
333 | };
|
334 |
|
335 | componentDidMount() {
|
336 | const { firestore } = this.props;
|
337 |
|
338 | firestore.doc('stories/1').onSnapshot(snapshot => {
|
339 | this.setState({ story: snapshot });
|
340 | });
|
341 | }
|
342 |
|
343 | render() {
|
344 | const { story } = this.state;
|
345 | const storyData = story ? story.data() : null;
|
346 |
|
347 | return storyData ? (
|
348 | <div>
|
349 | <h1>{storyData.title}</h1>
|
350 | <h2>
|
351 | {storyData.authorName} - {storyData.publishedDate}
|
352 | </h2>
|
353 | <p>{storyData.description}</p>
|
354 | </div>
|
355 | ) : (
|
356 | <Loading />
|
357 | );
|
358 | }
|
359 | }
|
360 |
|
361 | export default withFirestore(MyComponent);
|
362 | ```
|
363 |
|
364 | #### `Component.WrappedComponent`
|
365 |
|
366 | The wrapped component is available as a static property called
|
367 | `WrappedComponent` on the returned component. This can be used
|
368 | for testing the component in isolation, without needing to provide
|
369 | context in your tests.
|
370 |
|
371 | #### Props for returned component
|
372 |
|
373 | ##### wrappedComponentRef
|
374 |
|
375 | > function | _optional_
|
376 |
|
377 | A function that will be passed as the `ref` prop to the wrapped component.
|
378 |
|
379 | [npm]: https://www.npmjs.com/
|
380 | [yarn]: https://yarnpkg.com/
|
381 | [firestore-package]: https://www.npmjs.com/package/@firebase/firestore
|
382 | [firestore-class-docs]: https://cloud.google.com/nodejs/docs/reference/firestore/0.11.x/Firestore
|
383 | [create-react-app]: https://github.com/facebookincubator/create-react-app
|
384 | [build-badge]: https://img.shields.io/travis/green-arrow/react-firestore.svg?style=flat-square
|
385 | [build]: https://travis-ci.org/green-arrow/react-firestore
|
386 | [coverage-badge]: https://img.shields.io/codecov/c/github/green-arrow/react-firestore.svg?style=flat-square
|
387 | [coverage]: https://codecov.io/github/green-arrow/react-firestore
|
388 | [license-badge]: https://img.shields.io/npm/l/react-firestore.svg?style=flat-square
|
389 | [license]: https://github.com/green-arrow/react-firestore/blob/master/LICENSE
|
390 | [version-badge]: https://img.shields.io/npm/v/react-firestore.svg?style=flat-square
|
391 | [package]: https://www.npmjs.com/package/react-firestore
|
392 | [gzip-badge]: http://img.badgesize.io/https://unpkg.com/react-firestore/dist/react-firestore.umd.min.js?compression=gzip&label=gzip%20size&style=flat-square
|
393 | [size-badge]: http://img.badgesize.io/https://unpkg.com/react-firestore/dist/react-firestore.umd.min.js?label=size&style=flat-square
|
394 | [unpkg-dist]: https://unpkg.com/react-firestore/dist/
|
395 | [module-formats-badge]: https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20es-green.svg?style=flat-square
|