Routing Recipes
These recipes assume that you are using react-router, but the principles should apply to any routing solution.
Basic
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 a user to view a route if they are logged in):
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { compose } from 'redux'
import { connect } from 'react-redux'
import { isLoaded, isEmpty } from 'react-redux-firebase'
class ProtectedPage extends Component {
static propTypes = {
authExists: PropTypes.bool,
}
componentWillReceiveProps({ authExists }) {
if (authExists) {
this.context.router.push('/login') // redirect to /login if not authed
}
}
render() {
return (
<div>
You are authed!
</div>
)
}
}
export default connect(
({ firebase: { auth } }) => ({ authExists: !!auth && !!auth.uid })
)(ProtectedPage)
Advanced
Using 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).
Auth Required ("Route Protection")
In order to only allow authenticated users to view a page, a UserIsAuthenticated Higher Order Component can be created:
react-router v4 + redux-auth-wrapper v2
Make sure to install history using npm i --save history
import locationHelperBuilder from 'redux-auth-wrapper/history4/locationHelper';
import { connectedRouterRedirect } from 'redux-auth-wrapper/history4/redirect'
import createHistory from 'history/createBrowserHistory'
import LoadingScreen from 'components/LoadingScreen'; // change it to your custom component
const locationHelper = locationHelperBuilder({});
const browserHistory = createHistory()
export const UserIsAuthenticated = connectedRouterRedirect({
wrapperDisplayName: 'UserIsAuthenticated',
AuthenticatingComponent: LoadingScreen,
allowRedirectBack: true,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/login',
authenticatingSelector: ({ firebase: { auth, profile, isInitializing } }) =>
!auth.isLoaded || isInitializing === true,
authenticatedSelector: ({ firebase: { auth } }) =>
auth.isLoaded && !auth.isEmpty,
redirectAction: newLoc => (dispatch) => {
browserHistory.replace(newLoc); // or routerActions.replace
dispatch({ type: 'UNAUTHED_REDIRECT' });
},
});
export const UserIsNotAuthenticated = connectedRouterRedirect({
wrapperDisplayName: 'UserIsNotAuthenticated',
AuthenticatingComponent: LoadingScreen,
allowRedirectBack: false,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
authenticatingSelector: ({ firebase: { auth, isInitializing } }) =>
!auth.isLoaded || isInitializing === true,
authenticatedSelector: ({ firebase: { auth } }) =>
auth.isLoaded && auth.isEmpty,
redirectAction: newLoc => (dispatch) => {
browserHistory.replace(newLoc); // or routerActions.replace
dispatch({ type: 'UNAUTHED_REDIRECT' });
},
});
Then it can be used as a Higher Order Component wrapper on a component:
standard ES5/ES6
export default UserIsAuthenticated(ProtectedThing)
es7 decorators
@UserIsAuthenticated // redirects to '/login' if user not is logged in
export default class ProtectedThing extends Component {
render() {
return (
<div>
You are authed!
</div>
)
}
}
Or it can be used at the route level:
<Route path="/" component={App}>
<Route path="login" component={Login}/>
<Route path="foo" component={UserIsAuthenticated(Foo)}/>
</Route>
redux-auth-wrapper v1
import { browserHistory } from 'react-router'
import { UserAuthWrapper } from 'redux-auth-wrapper'
export const UserIsAuthenticated = UserAuthWrapper({
wrapperDisplayName: 'UserIsAuthenticated',
authSelector: ({ firebase: { auth } }) => auth,
authenticatingSelector: ({ firebase: { auth, isInitializing } }) =>
!auth.isLoaded || isInitializing === true,
predicate: auth => !auth.isEmpty,
redirectAction: (newLoc) => (dispatch) => {
browserHistory.replace(newLoc)
// routerActions.replace // if using react-router-redux
dispatch({
type: 'UNAUTHED_REDIRECT',
payload: { message: 'You must be authenticated.' },
})
}
})
Redirect Authenticated
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.
react-router v4 + redux-auth-wrapper v2
import locationHelperBuilder from 'redux-auth-wrapper/history4/locationHelper';
import { connectedRouterRedirect } from 'redux-auth-wrapper/history4/redirect'
import createHistory from 'history/createBrowserHistory'
import LoadingScreen from 'components/LoadingScreen'; // change it to your custom component
const locationHelper = locationHelperBuilder({})
const history = createHistory()
export const UserIsNotAuthenticated = connectedRouterRedirect({
wrapperDisplayName: 'UserIsNotAuthenticated',
AuthenticatingComponent: LoadingScreen,
allowRedirectBack: false,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
authenticatingSelector: ({ firebase: { auth, isInitializing } }) =>
!auth.isLoaded || isInitializing === true,
authenticatedSelector: ({ firebase: { auth } }) =>
auth.isLoaded && auth.isEmpty,
redirectAction: newLoc => (dispatch) => {
history.push(newLoc)
// routerActions.replace or other redirect
dispatch({ type: 'UNAUTHED_REDIRECT' });
},
});
Can then be used on a Login route component:
import React from 'react'
import { useFirebase } from 'react-redux-firebase'
function Login() {
const firebase = useFirebase()
return (
<div>
<button onClick={() => firebase.login({ provider: 'google' })}>
Google Login
</button>
</div>
)
}
// redirects to '/' if user is logged in
export default UserIsNotAuthenticated
react-router v3 and earlier + redux-auth-wrapper v1
import { browserHistory } from 'react-router'
import { UserAuthWrapper } from 'redux-auth-wrapper'
import LoadingScreen from '../components/LoadingScreen'; // change it to your custom component
export const UserIsNotAuthenticated = UserAuthWrapper({
failureRedirectPath: '/',
authSelector: ({ firebase: { auth } }) => auth,
authenticatingSelector: ({ firebase: { auth, isInitializing } }) =>
!auth.isLoaded || isInitializing === true,
predicate: auth => auth.isEmpty,
redirectAction: (newLoc) => (dispatch) => {
browserHistory.replace(newLoc)
dispatch({
type: 'AUTHED_REDIRECT',
payload: { message: 'User is authenticated. Redirecting home...' }
})
}
})