Skip to content

Commit 62daf9c

Browse files
committed
Docs updated with information about using with redux-auth-wrapper.
1 parent 3e2de97 commit 62daf9c

File tree

4 files changed

+231
-12
lines changed

4 files changed

+231
-12
lines changed

README.md

+67-11
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ import { connect } from 'react-redux'
112112
import { firebaseConnect, helpers } from 'react-redux-firebase'
113113
const { isLoaded, isEmpty, dataToJS } = helpers
114114

115-
@firebaseConnect( [
115+
@firebaseConnect([
116116
'/todos'
117117
// { path: '/todos' } // object notation
118118
])
@@ -211,9 +211,11 @@ An example that user Material UI built on top of the output of [create-react-app
211211

212212
## Discussion
213213

214-
Join the [redux-firebase gitter](https://gitter.im/redux-firebase/Lobby).
214+
Join us on the [redux-firebase gitter](https://gitter.im/redux-firebase/Lobby).
215215

216-
## Using with `redux-thunk`
216+
## Using With Other Libraries
217+
218+
### redux-thunk
217219
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:
218220

219221
createStore:
@@ -244,23 +246,22 @@ const store = createStore(
244246
Action:
245247

246248
```javascript
247-
const sendNotification = (payload) => {
248-
type: NOTIFICATION,
249-
payload
250-
}
251249
export const addTodo = (newTodo) =>
252250
(dispatch, getState, getFirebase) => {
253251
const firebase = getFirebase()
254252
firebase
255253
.push('todos', newTodo)
256254
.then(() => {
257-
dispatch(sendNotification('Todo Added'))
255+
dispatch({
256+
type: NOTIFICATION,
257+
payload
258+
})
258259
})
259260
};
260261

261262
```
262263

263-
## Using with `redux-observable`
264+
### redux-observable
264265
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:
265266

266267
```javascript
@@ -278,11 +279,56 @@ const somethingEpic = (action$, store, getFirebase) =>
278279
)
279280
```
280281

282+
### redux-auth-wrapper
283+
284+
*For full example, go to the [Routing Recipes Section of the docs](http://react-redux-firebase.com/docs/recipes/routing.html)*
285+
286+
In order to only allow authenticated users to view a page, a `UserIsAuthenticated` Higher Order Component can be created:
287+
288+
```javascript
289+
import { browserHistory } from 'react-router'
290+
import { UserAuthWrapper } from 'redux-auth-wrapper'
291+
import { helpers } from 'react-redux-firebase'
292+
const { pathToJS } = helpers
293+
294+
export const UserIsAuthenticated = UserAuthWrapper({
295+
wrapperDisplayName: 'UserIsAuthenticated',
296+
authSelector: ({ firebase }) => pathToJS(firebase, 'auth'),
297+
authenticatingSelector: ({ firebase }) => pathToJS(firebase, 'isInitializing') === true,
298+
redirectAction: (newLoc) => (dispatch) => {
299+
browserHistory.replace(newLoc)
300+
dispatch({
301+
type: 'UNAUTHED_REDIRECT',
302+
payload: { message: 'You must be authenticated.' },
303+
})
304+
},
305+
})
306+
```
307+
308+
Then it can be used as a Higher Order Component wrapper on a component:
309+
310+
```javascript
311+
@UserIsAuthenticated // redirects to '/login' if user not is logged in
312+
export default class ProtectedThing extends Component {
313+
render() {
314+
return (
315+
<div>
316+
You are authed!
317+
</div>
318+
)
319+
}
320+
}
321+
```
322+
281323
## Starting A Project
282324

283325
### Generator
284326

285-
[generator-react-firebase](https://github.com/prescottprue/generator-react-firebase) is a yeoman generator uses react-redux-firebase when opting to include redux
327+
[generator-react-firebase](https://github.com/prescottprue/generator-react-firebase) is a yeoman generator uses react-redux-firebase when opting to include redux.
328+
329+
### Complete Examples
330+
331+
The [examples folder](/examples) contains full applications that can be copied/adapted and used as a new project.
286332

287333
## FAQ
288334

@@ -306,12 +352,22 @@ const somethingEpic = (action$, store, getFirebase) =>
306352

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

355+
3. Where can I find some examples?
356+
357+
* [Recipes Section](http://react-redux-firebase.com/docs/recipes/) of [the docs](http://react-redux-firebase.com/docs/recipes/)
358+
* [examples folder](/examples) contains [complete example apps](/examples/complete) as well as [useful snippets](/examples/snippets)
359+
360+
4. How do I help?
361+
362+
* Join the conversion on [gitter][gitter-url]
363+
* Post Issues
364+
* Create Pull Requests
309365

310366
# Patrons
311367

312368
Meet some of the outstanding companies and individuals that made it possible:
313369

314-
* [Reside Network Inc.](https://github.com/reside-eng)
370+
* [Reside Network Inc.](https://github.com/reside-eng)
315371

316372

317373
## Contributors

SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* [Actions](/docs/recipes/actions.md)
1313
* [Thunks](/docs/recipes/thunks.md)
1414
* [Epics](/docs/recipes/epics.md)
15+
* [Routing](/docs/recipes/routing.md)
1516
* [Redux Form](/docs/recipes/redux-form.md)
1617
* [Populate](/docs/recipes/populate.md)
1718
* [API Reference](/docs/api/README.md)

docs/recipes/README.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Recipes
22

3-
This section includes some recipes for using react-redux-firebase within real applications.
3+
This section includes some recipes for using `react-redux-firebase` within real applications.
44

55
## [Profile](/docs/recipes/profile.md)
66
Recipes for using/modifying built in profile handling.
@@ -44,3 +44,12 @@ Middleware that listens for Actions and dispatches other, often async, actions.
4444
* Debounced persisting of user input: Listen for typing actions from `redux-form` -> call `firebase.update()`
4545
* Throttled/Debounced API calls
4646
* Displaying a system wide error: Listen for error actions -> display error message
47+
48+
## [Routing](/docs/recipes/routing.md)
49+
50+
Change location within your application based on Firebase state.
51+
52+
#### Examples
53+
* Route Protection (user redirected to `/login` if not authenticated)
54+
* Redirect to `/` if user visits `/login` when authenticated
55+
* Show/Hide components based on user profile data such as `role: 'admin'` or `isAdmin: true`

docs/recipes/routing.md

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Routing Recipes
2+
3+
These recipes assume that you are using [`react-router`](https://github.com/ReactTraining/react-router), but the principles should be applicable to any routing solution.
4+
5+
## Basic
6+
7+
Routing can be changed based on data by using react lifecycle hooks such as `componentWillMount`, and `componentWillReceiveProps` to route users. This can be particularly useful when doing things such as route protection (only allowing user to view a route if they are logged in):
8+
9+
```javascript
10+
import React, { Component, PropTypes } from 'react'
11+
import { connect } from 'react-redux'
12+
import { firebaseConnect, helpers } from 'react-redux-firebase'
13+
14+
const { isLoaded, isEmpty, pathToJS } = helpers
15+
16+
@firebaseConnect()
17+
@connect(
18+
({ firebase }) => ({
19+
auth: pathToJS(firebase, 'auth'),
20+
})
21+
)
22+
export default class ProtectedPage extends Component {
23+
static propTypes = {
24+
auth: PropTypes.object,
25+
}
26+
27+
componentWillReceiveProps({ auth }) {
28+
if (auth && !auth.uid) {
29+
this.context.router.push('/login') // redirect to /login if not authed
30+
}
31+
}
32+
33+
render() {
34+
return (
35+
<div>
36+
You are authed!
37+
</div>
38+
)
39+
}
40+
}
41+
```
42+
43+
## Advanced
44+
45+
Using [`redux-auth-wrapper`](https://github.com/mjrussell/redux-auth-wrapper) you can easily create a Higher Order Component (wrapper) that can be used to redirect users based on Firebase state (including auth).
46+
47+
### Auth Required ("Route Protection")
48+
49+
In order to only allow authenticated users to view a page, a `UserIsAuthenticated` Higher Order Component can be created:
50+
51+
```javascript
52+
import { browserHistory } from 'react-router'
53+
import { UserAuthWrapper } from 'redux-auth-wrapper'
54+
import { helpers } from 'react-redux-firebase'
55+
const { pathToJS } = helpers
56+
57+
export const UserIsAuthenticated = UserAuthWrapper({
58+
wrapperDisplayName: 'UserIsAuthenticated',
59+
authSelector: ({ firebase }) => pathToJS(firebase, 'auth'),
60+
authenticatingSelector: ({ firebase }) => pathToJS(firebase, 'isInitializing') === true,
61+
redirectAction: (newLoc) => (dispatch) => {
62+
browserHistory.replace(newLoc)
63+
dispatch({
64+
type: 'UNAUTHED_REDIRECT',
65+
payload: { message: 'You must be authenticated.' },
66+
})
67+
},
68+
})
69+
```
70+
71+
Then it can be used as a Higher Order Component wrapper on a component:
72+
73+
*es7 decorators*
74+
75+
```javascript
76+
@UserIsAuthenticated // redirects to '/login' if user not is logged in
77+
export default class ProtectedThing extends Component {
78+
render() {
79+
return (
80+
<div>
81+
You are authed!
82+
</div>
83+
)
84+
}
85+
}
86+
```
87+
88+
*standard ES5/ES6*
89+
90+
```javascript
91+
export default UserIsAuthenticated(ProtectedThing)
92+
```
93+
94+
Or it can be used at the route level:
95+
96+
```javascript
97+
<Route path="/" component={App}>
98+
<Route path="login" component={Login}/>
99+
<Route path="foo" component={UserIsAuthenticated(Foo)}/>
100+
</Route>
101+
```
102+
103+
104+
### Redirect Authenticated
105+
Just as easily as creating a wrapper for redirect if a user is not logged in, we can create one that redirects if a user *IS* authenticated. This can be useful for pages that you do not want a logged in user to see, such as the login page.
106+
107+
```javascript
108+
import { browserHistory } from 'react-router'
109+
import { UserAuthWrapper } from 'redux-auth-wrapper'
110+
import { helpers } from 'react-redux-firebase'
111+
const { pathToJS } = helpers
112+
113+
export const UserIsNotAuthenticated = UserAuthWrapper({
114+
wrapperDisplayName: 'UserIsNotAuthenticated',
115+
allowRedirectBack: false,
116+
failureRedirectPath: '/',
117+
authSelector: ({ firebase }) => pathToJS(firebase, 'auth'),
118+
authenticatingSelector: ({ firebase }) => pathToJS(firebase, 'isInitializing') !== true,
119+
predicate: auth => auth === null,
120+
redirectAction: (newLoc) => (dispatch) => {
121+
browserHistory.replace(newLoc)
122+
dispatch({
123+
type: 'AUTHED_REDIRECT',
124+
payload: { message: 'User is authenticated. Redirecting home...' }
125+
})
126+
}
127+
})
128+
129+
```
130+
131+
Can then be used on a Login route component:
132+
133+
```javascript
134+
import React, { Component } from 'react'
135+
import { firebaseConnect } from 'react-redux-firebase'
136+
137+
@firebaseConnect() // adds this.props.firebase
138+
@UserIsNotAuthenticated // redirects to '/' if user is logged in
139+
export default class Login extends Component {
140+
googleLogin = () => {
141+
this.props.firebase.login({ provider: 'google' })
142+
}
143+
render() {
144+
return (
145+
<div>
146+
<button onClick={this.googleLogin}>
147+
Google Login
148+
</button>
149+
</div>
150+
)
151+
}
152+
}
153+
```

0 commit comments

Comments
 (0)