Skip to content

Commit e2bd8da

Browse files
authored
Version v1.1.0
* Populate functionality now includes object notation like so: ```js {path: '/todos', populates: [{ child: 'owner', root: 'users' }] ``` * `reactReduxFirebase` function now passes through `enhancer` argument of `createStore` * email now included in default user profile with external providers * `onAuthStateChanged` function exposed in config (for setting auth info to other libraries such as [`react-ga`](https://www.npmjs.com/package/react-ga)) * Coverage Calculation fixed (now using `istanbul` directly) * Tests added for utils * Multiple internal optimizations * `firebase` and `lodash` dependencies updated * `no-console` rule added to `.eslintrc` to keep `console` out of src * README updated with `redux-observable` integration
2 parents 976484d + d2de64f commit e2bd8da

29 files changed

+748
-426
lines changed

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
},
2020
"rules": {
2121
"semi" : [2, "never"],
22+
"no-console": "error"
2223
}
2324
}

README.md

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ View deployed version of Material Example [here](https://redux-firebasev3.fireba
2626
- queries support ( `orderByChild`, `orderByKey`, `orderByValue`, `orderByPriority`, `limitToLast`, `limitToFirst`, `startAt`, `endAt`, `equalTo` right now )
2727
- Automatic binding/unbinding
2828
- Declarative decorator syntax for React components
29-
- [`redux-thunk`](https://github.com/gaearon/redux-thunk) Integration
29+
- [`redux-thunk`](https://github.com/gaearon/redux-thunk) and [redux-observable](https://redux-observable.js.org/) integrations
30+
- Action Types and other Constants exported for external use (such as in redux-observable)
3031
- Firebase v3+ support
3132

3233
## Install
@@ -78,7 +79,7 @@ Include reduxFirebase in your store compose function:
7879

7980
```javascript
8081
import { createStore, combineReducers, compose } from 'redux'
81-
import { reduxFirebase, firebaseStateReducer } from 'react-redux-firebase'
82+
import { reactReduxFirebase, firebaseStateReducer } from 'react-redux-firebase'
8283

8384
// Add Firebase to reducers
8485
const rootReducer = combineReducers({
@@ -95,7 +96,7 @@ const config = {
9596

9697
// Add redux Firebase to compose
9798
const createStoreWithFirebase = compose(
98-
reduxFirebase(config, { userProfile: 'users' }),
99+
reactReduxFirebase(config, { userProfile: 'users' }),
99100
)(createStore)
100101

101102
// Create store with reducers and initial state
@@ -106,21 +107,16 @@ In components:
106107
```javascript
107108
import React, { Component, PropTypes } from 'react'
108109
import { connect } from 'react-redux'
109-
import { firebase, helpers } from 'react-redux-firebase'
110+
import { firebaseConnect, helpers } from 'react-redux-firebase'
110111
const { isLoaded, isEmpty, dataToJS } = helpers
111112

112-
// Can be used if firebase is used elsewhere
113-
// import { firebaseConnect } from 'react-redux-firebase'
114-
// @firebaseConnect( [
115-
// '/todos'
116-
// ])
117-
118-
@firebase( [
113+
@firebaseConnect( [
119114
'/todos'
120115
// { type: 'once', path: '/todos' } // for loading once instead of binding
121116
])
122117
@connect(
123-
({firebase}) => ({
118+
({ firebase }) => ({
119+
// Connect todos prop to firebase todos
124120
todos: dataToJS(firebase, '/todos'),
125121
})
126122
)
@@ -172,7 +168,7 @@ Alternatively, if you choose not to use decorators:
172168

173169
```javascript
174170

175-
const wrappedTodos = firebase([
171+
const wrappedTodos = firebaseConnect([
176172
'/todos'
177173
])(Todos)
178174
export default connect(
@@ -201,14 +197,14 @@ The simple example implemented using decorators built from the output of [create
201197
An example that user Material UI built on top of the output of [create-react-app](https://github.com/facebookincubator/create-react-app)'s eject command. Shows a list of todo items and allows you to add to them. This is what is deployed to [react-redux-firebase.firebaseapp.com](https://react-redux-firebase.firebaseapp.com/).
202198

203199
## Using with `redux-thunk`
204-
If you user using `redux-thunk`, make sure to set up your thunk middleware using it's redux-thunk's `withExtraArgument` method so that firebase is available within your actions. Here is an example `createStore` function that adds `getFirebase` as third argument along with a thunk that uses it:
200+
If you are using `redux-thunk`, make sure to set up your thunk middleware using it's redux-thunk's `withExtraArgument` method so that firebase is available within your actions. Here is an example `createStore` function that adds `getFirebase` as third argument along with a thunk that uses it:
205201

206202
createStore:
207203

208204
```javascript
209205
import { applyMiddleware, compose, createStore } from 'redux';
210206
import thunk from 'redux-thunk';
211-
import { reduxReactFirebase, getFirebase } from 'react-redux-firebase';
207+
import { reactReduxFirebase, getFirebase } from 'react-redux-firebase';
212208
import makeRootReducer from './reducers';
213209

214210
const fbConfig = {} // your firebase config
@@ -220,7 +216,7 @@ const store = createStore(
220216
applyMiddleware([
221217
thunk.withExtraArgument(getFirebase) // Pass getFirebase function as extra argument
222218
]),
223-
reduxReactFirebase(fbConfig, { userProfile: 'users', enableLogging: false })
219+
reactReduxFirebase(fbConfig, { userProfile: 'users', enableLogging: false })
224220
)
225221
);
226222

@@ -244,6 +240,24 @@ export const addTodo = (newTodo) =>
244240

245241
```
246242

243+
## Using with `redux-observable`
244+
If you are using `redux-observable`, make sure to set up your redux-observable middleware so that firebase is available within your epics. Here is an example `combineEpics` function that adds `getFirebase` as third argument along with an epic that uses it:
245+
246+
```javascript
247+
import { getFirebase } from 'react-redux-firebase'
248+
import { combineEpics } from 'redux-observable'
249+
250+
const rootEpic = (...args) =>
251+
combineEpics(somethingEpic, epic2)(..args, getFirebase)
252+
253+
// then later in your epics
254+
const somethingEpic = (action$, store, getFirebase) =>
255+
action$.ofType(SOMETHING)
256+
.map(() =>
257+
getFirebase().push('somePath/onFirebase', { some: 'data' })
258+
)
259+
```
260+
247261
## Generator
248262

249263
[generator-react-firebase](https://github.com/prescottprue/generator-react-firebase) uses react-redux-firebase when opting to include redux
@@ -256,22 +270,21 @@ export const addTodo = (newTodo) =>
256270
* [populate functionality](https://prescottprue.gitbooks.io/react-redux-firebase/content/populate.html) (similar to mongoDB or SQL JOIN)
257271
* [`profileDecorator`](https://prescottprue.gitbooks.io/react-redux-firebase/content/config.html) - change format of profile stored on Firebase
258272
* [`getFirebase`](https://prescottprue.gitbooks.io/react-redux-firebase/content/thunks.html) - access to firebase instance that fires actions when methods are called
259-
* [capability for thunk integration](https://prescottprue.gitbooks.io/react-redux-firebase/content/thunks.html) - using `getFirebase` and `thunk.withExtraArgument`
273+
* [integrations](https://prescottprue.gitbooks.io/react-redux-firebase/content/thunks.html) for [`redux-thunk`](https://github.com/gaearon/redux-thunk) and [`redux-observable`](https://redux-observable.js.org) - using `getFirebase`
260274
* [access to firebase's `storage`](https://prescottprue.gitbooks.io/react-redux-firebase/content/storage.html) method
261275
* `uniqueSet` method helper for only setting if location doesn't already exist
276+
* Object or String notation for paths (`[{ path: '/todos' }]` equivalent to `['/todos']`)
277+
* Action Types and other Constants are exposed for external usage (such as `redux-observable`)
262278

263279
#### Well why not combine?
264-
I am in the process of writing up an article comparing the two, including the difference in defaults/utils. Maybe a section should be included in the docs as well?
265-
266-
Also, I have been talking to the author of redux-react-firebase about combining, but we are not sure that the users of both want that at this point. Join us on [the redux-firebase gitter](https://gitter.im/redux-firebase/Lobby) if you haven't already since a ton of this type of discussion goes on there.
280+
I have been talking to the author of [redux-react-firebase]() about combining, but we are not sure that the users of both want that at this point. Join us on [the redux-firebase gitter](https://gitter.im/redux-firebase/Lobby) if you haven't already since a ton of this type of discussion goes on there.
267281

268282
**Bottom line:** The author of redux-react-firebase was absent when functionality was needed by me and others, so this library was created.
269283

270284
2. Why use redux if I have Firebase to store state?
271285

272286
This isn't a super quick answer, so I wrote up [a medium article to explain](https://medium.com/@prescottprue/firebase-with-redux-82d04f8675b9)
273287

274-
275288
## Contributors
276289
- [Prescott Prue](https://github.com/prescottprue)
277290
- [Tiberiu Craciun](https://github.com/tiberiuc)
@@ -281,7 +294,7 @@ export const addTodo = (newTodo) =>
281294

282295
## Thanks
283296

284-
Special thanks to [Tiberiu Craciun](https://github.com/tiberiuc) for creating [redux-react-firebase](https://github.com/tiberiuc/redux-react-firebase), which this project is heavily based on.
297+
Special thanks to [Tiberiu Craciun](https://github.com/tiberiuc) for creating [redux-react-firebase](https://github.com/tiberiuc/redux-react-firebase), which this project was originally based on.
285298

286299
[npm-image]: https://img.shields.io/npm/v/react-redux-firebase.svg?style=flat-square
287300
[npm-url]: https://npmjs.org/package/react-redux-firebase

examples/decorators/src/store.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import { createStore, compose } from 'redux'
22
import rootReducer from './reducer'
33
import { firebase as fbConfig } from './config'
4-
import { reduxReactFirebase } from 'react-redux-firebase'
5-
// import { reduxFirebase } from 'redux-firebase' // >= v0.1.1
4+
import { reactReduxFirebase } from 'react-redux-firebase'
65

76
export default function configureStore (initialState, history) {
87
const createStoreWithMiddleware = compose(
9-
reduxReactFirebase(fbConfig, { userProfile: 'users', enableLogging: false }),
10-
// reduxFirebase(fbConfig, { userProfile: 'users', enableLogging: false }) // >= v0.1.1
8+
reactReduxFirebase(fbConfig,
9+
{
10+
userProfile: 'users',
11+
enableLogging: false
12+
}
13+
),
1114
typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f
1215
)(createStore)
1316
const store = createStoreWithMiddleware(rootReducer)

examples/material/src/store/configureStore.dev.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@ import { createStore, applyMiddleware, compose } from 'redux'
22
import rootReducer from '../reducers'
33
import { firebase as fbConfig } from '../config'
44
import { syncHistory } from 'react-router-redux'
5-
import { reduxFirebase } from 'react-redux-firebase' // >= v0.1.1
5+
import { reactReduxFirebase } from 'react-redux-firebase' // >= v0.1.1
66

77
export default function configureStore (initialState, history) {
88
const reduxRouterMiddleware = syncHistory(history)
99
const createStoreWithMiddleware = compose(
1010
applyMiddleware(reduxRouterMiddleware),
11-
reduxFirebase(fbConfig, { userProfile: 'users', enableLogging: false }),
11+
reactReduxFirebase(fbConfig,
12+
{
13+
userProfile: 'users',
14+
enableLogging: false
15+
}
16+
),
1217
typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f
1318
)(createStore)
1419
const store = createStoreWithMiddleware(rootReducer, initialState)

examples/material/src/store/configureStore.prod.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import { createStore, compose } from 'redux'
22
import rootReducer from '../reducers'
33
import { firebase as fbConfig } from '../config'
4-
import { reduxReactFirebase } from 'react-redux-firebase' // >= v0.1.0
5-
// import { reduxFirebase } from 'redux-firebase' // >= v0.1.1
4+
import { reduxReactFirebase } from 'react-redux-firebase'
65

76
export default function configureStore (initialState, history) {
87
const createStoreWithMiddleware = compose(
9-
reduxReactFirebase(fbConfig, { userProfile: 'users' }) // >= v0.1.0
10-
// reduxFirebase(fbConfig, { userProfile: 'users' }) // >= v0.1.1
8+
reactReduxFirebase(fbConfig,
9+
{
10+
userProfile: 'users',
11+
enableLogging: false
12+
}
13+
),
1114
)(createStore)
1215
const store = createStoreWithMiddleware(rootReducer, initialState)
1316

examples/simple/src/App.js

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import TodoItem from './TodoItem'
55

66
// redux/firebase
77
import { connect } from 'react-redux'
8-
import { firebase, helpers } from 'react-redux-firebase'
8+
import { firebaseConnect, helpers } from 'react-redux-firebase'
99
const { isLoaded, isEmpty, pathToJS, dataToJS } = helpers
1010

1111
class App extends Component {
@@ -15,14 +15,18 @@ class App extends Component {
1515
push: PropTypes.func.isRequired
1616
})
1717
}
18+
19+
handleAdd = () => {
20+
const { firebase } = this.props
21+
const { newTodo } = this.refs
22+
firebase.push('/todos', { text: newTodo.value, done: false })
23+
newTodo.value = ''
24+
}
25+
1826
render () {
19-
const { firebase, todos } = this.props
27+
const { todos } = this.props
2028

21-
const handleAdd = () => {
22-
const { newTodo } = this.refs
23-
firebase.push('/todos', { text: newTodo.value, done: false })
24-
newTodo.value = ''
25-
}
29+
console.log('todos;', todos)
2630

2731
const todosList = (!isLoaded(todos))
2832
? 'Loading'
@@ -50,22 +54,25 @@ class App extends Component {
5054
{todosList}
5155
<h4>New Todo</h4>
5256
<input type='text' ref='newTodo' />
53-
<button onClick={handleAdd}>Add</button>
57+
<button onClick={this.handleAdd}>
58+
Add
59+
</button>
5460
</div>
5561
</div>
5662
)
5763
}
5864
}
59-
const fbWrappedComponent = firebase([
60-
'/todos'
65+
const fbWrappedComponent = firebaseConnect([
66+
// '/todos'
6167
// { type: 'once', path: '/todos' } // for loading once instead of binding
6268
// '/todos#populate=owner:displayNames' // for populating owner parameter from id into string loaded from /displayNames root
63-
// '/todos#populate=owner:users' // for populating owner parameter from id to user object loaded from /users root
69+
// '/todos#populate=collaborators:users' // for populating owner parameter from id to user object loaded from /users root
70+
{ path: 'todos', populates: [{ child: 'collaborators', root: 'users' }] } // object notation
6471
// '/todos#populate=owner:users:displayName' // for populating owner parameter from id within to displayName string from user object within users root
6572
])(App)
6673

6774
export default connect(
68-
({firebase}) => ({
75+
({ firebase }) => ({
6976
todos: dataToJS(firebase, 'todos'),
7077
profile: pathToJS(firebase, 'profile'),
7178
auth: pathToJS(firebase, 'auth')

examples/simple/src/config.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
export const firebase = {
22
apiKey: 'AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots',
3-
authDomain: 'react-redux-firebase.firebaseapp.com',
4-
databaseURL: 'https://react-redux-firebase.firebaseio.com'
3+
authDomain: 'redux-firebasev3.firebaseapp.com',
4+
databaseURL: 'https://redux-firebasev3.firebaseio.com',
5+
storageBucket: 'redux-firebasev3.appspot.com',
6+
messagingSenderId: '823357791673'
57
}
68

79
export default { firebase }

examples/simple/src/store.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { createStore, compose } from 'redux'
22
import rootReducer from './reducer'
33
import { firebase as fbConfig } from './config'
4-
import { reduxFirebase } from 'react-redux-firebase'
4+
import { reactReduxFirebase } from 'react-redux-firebase'
55

66
export default function configureStore (initialState, history) {
77
const createStoreWithMiddleware = compose(
8-
reduxFirebase(fbConfig, { userProfile: 'users' }),
8+
reactReduxFirebase(fbConfig,
9+
{
10+
userProfile: 'users',
11+
enableLogging: false
12+
}
13+
),
914
typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f
1015
)(createStore)
1116
const store = createStoreWithMiddleware(rootReducer)

package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-redux-firebase",
3-
"version": "1.0.3",
3+
"version": "1.1.0",
44
"description": "Redux integration for Firebase. Comes with a Higher Order Component for use with React.",
55
"main": "dist/index.js",
66
"module": "src/index.js",
@@ -14,7 +14,7 @@
1414
"lint": "eslint src/**",
1515
"lint:fix": "npm run lint -- --fix",
1616
"test": "mocha -R spec --compilers js:babel-core/register ./test/setup.js ./test/**/*.spec.js",
17-
"test:cov": "babel-node $(npm bin)/isparta cover --report lcov ./node_modules/mocha/bin/_mocha -- ./test/** --recursive",
17+
"test:cov": "istanbul cover ./node_modules/mocha/bin/_mocha -- ./test/** --recursive --report lcov --compilers js:babel-register --require babel-polyfill",
1818
"codecov": "cat coverage/*/lcov.info | codecov",
1919
"prepush": "npm run lint:fix"
2020
},
@@ -39,10 +39,10 @@
3939
}
4040
],
4141
"dependencies": {
42-
"firebase": "^3.5.3",
42+
"firebase": "^3.6.1",
4343
"immutable": "^3.8.1",
4444
"jwt-decode": "^2.1.0",
45-
"lodash": "^4.16.6"
45+
"lodash": "^4.17.2"
4646
},
4747
"peerDependencies": {
4848
"react": "^0.14.6 || ^15.0.0",
@@ -51,7 +51,7 @@
5151
},
5252
"devDependencies": {
5353
"babel-cli": "^6.18.0",
54-
"babel-core": "^6.18.0",
54+
"babel-core": "^6.18.2",
5555
"babel-eslint": "^7.1.0",
5656
"babel-plugin-add-module-exports": "^0.2.1",
5757
"babel-plugin-es6-promise": "^1.0.0",
@@ -62,18 +62,18 @@
6262
"babel-preset-stage-1": "^6.16.0",
6363
"chai": "^3.5.0",
6464
"codecov": "^1.0.1",
65-
"eslint": "^3.9.0",
65+
"eslint": "^3.10.2",
6666
"eslint-config-standard": "^6.2.1",
6767
"eslint-config-standard-react": "^4.2.0",
68-
"eslint-plugin-babel": "^3.3.0",
68+
"eslint-plugin-babel": "^4.0.0",
6969
"eslint-plugin-promise": "^3.0.0",
70-
"eslint-plugin-react": "^6.4.1",
70+
"eslint-plugin-react": "^6.7.1",
7171
"eslint-plugin-standard": "^2.0.1",
72-
"isparta": "^4.0.0",
72+
"istanbul": "^1.1.0-alpha.1",
7373
"jsdom": "^9.8.3",
7474
"mocha": "^3.1.2",
75-
"react-addons-test-utils": "^15.3.2",
76-
"react-dom": "^15.3.2",
75+
"react-addons-test-utils": "^15.4.0",
76+
"react-dom": "^15.4.0",
7777
"rimraf": "^2.5.4"
7878
},
7979
"license": "MIT",

0 commit comments

Comments
 (0)