I am building a React + Redux + Firebase app with webpack and am trying to figure out the best way to handle protected routes and redirects. I have tried using onEnter()
with setTimeout()
like many examples show, but it still ended up flashing the alternate ponent before redirecting.
Here is what I currently have, and I am trying to e up with a more elegant solution. This mostly works, but an example of when it fails is if I am on /profile
and navigate to /
in the browser. It seems that because firebase.auth()
needs to initialize again, I get a flash of the HomePage
ponent before it switches to Profile
.
Firebase is initialized in firebaseClientConfig
and I am passing firebase.auth()
in to routes
.
import { auth } from './firebaseClientConfig';
...
export default (
<Route path="/" ponent={App}>
<IndexRoute
getComponent={(nextState, cb) => {
auth.onAuthStateChanged((user) => {
if (!user) {
return require.ensure([], require => {
cb(null, require('./ponents/HomePage').default);
});
} else {
return require.ensure([], require => {
cb(null, require('./modules/Profile/Profile').default);
});
}
});
}}
/>
<Route
path="/profile"
getComponent={(nextState, cb) => {
auth.onAuthStateChanged((user) => {
if (user) {
return require.ensure([], require => {
cb(null, require('./modules/Profile/Profile').default);
});
} else {
return require.ensure([], require => {
cb(null, require('./modules/Login/Login').default);
});
}
});
}}
/>
</Route>
);
Does anyone have a better way of doing this?
I am building a React + Redux + Firebase app with webpack and am trying to figure out the best way to handle protected routes and redirects. I have tried using onEnter()
with setTimeout()
like many examples show, but it still ended up flashing the alternate ponent before redirecting.
Here is what I currently have, and I am trying to e up with a more elegant solution. This mostly works, but an example of when it fails is if I am on /profile
and navigate to /
in the browser. It seems that because firebase.auth()
needs to initialize again, I get a flash of the HomePage
ponent before it switches to Profile
.
Firebase is initialized in firebaseClientConfig
and I am passing firebase.auth()
in to routes
.
import { auth } from './firebaseClientConfig';
...
export default (
<Route path="/" ponent={App}>
<IndexRoute
getComponent={(nextState, cb) => {
auth.onAuthStateChanged((user) => {
if (!user) {
return require.ensure([], require => {
cb(null, require('./ponents/HomePage').default);
});
} else {
return require.ensure([], require => {
cb(null, require('./modules/Profile/Profile').default);
});
}
});
}}
/>
<Route
path="/profile"
getComponent={(nextState, cb) => {
auth.onAuthStateChanged((user) => {
if (user) {
return require.ensure([], require => {
cb(null, require('./modules/Profile/Profile').default);
});
} else {
return require.ensure([], require => {
cb(null, require('./modules/Login/Login').default);
});
}
});
}}
/>
</Route>
);
Does anyone have a better way of doing this?
Share Improve this question asked Sep 6, 2016 at 0:41 BonitisBonitis 1531 gold badge3 silver badges9 bronze badges 4- 1 That's pretty elegant, nice! – Michael Bushe Commented Oct 19, 2016 at 7:50
- @Bonitis I heard redux does not go well with firebase – ey dee ey em Commented Nov 1, 2017 at 15:31
- @Ezeewei they actually work together beautifully! This question is outdated because react-router has gone from v3 -> v4, so there are major changes. In general, I have had no issues with redux+firebase. – Bonitis Commented Nov 2, 2017 at 12:56
- @Bonitis ah nice! that's a really good news, myself was hesitating intergrating them together. thanks! – ey dee ey em Commented Nov 2, 2017 at 13:54
3 Answers
Reset to default 4I made a PrivateRoute
ponent and pass it an authenticated
prop which I get from the redux store in the ponent where I use the PrivateRoute
When I login with firebase I put user data in the redux store. So in my case i would map state to props
const mapStateToProps = (state) => ({
authenticated: !!state.user.uid,
})
And give the authenticated prop to my PrivateRoute
ponent
const PrivateRoute = ({ ponent: Component, authenticated, path, exact }) => {
return (
<Route
path={path}
exact={exact}
render={props =>
authenticated
? <Component {...props} />
: <Redirect to="/login"/>}
/>
)
}
if you are managing authentication using local state instead of redux then you need to create two states to hold progress of your authentication.
state={"loading=true",
"loggedin=false"}
i.e. redner Loading ponent initialy when loading is true and when i receive current user state from firebase function i will use setState() to set loading to flase and further render the ponent i intend to render according to the user state.
For better understanding have look react firebase authentication
I know is kind of late, but this might help others: You can find a slightly different approach here (still with a private route so kind of similar to vhflat but I find it a little more clear and easy to implement).
Basically, in your private route you just
import { useSelector } from "react-redux";
and
import { isLoaded } from 'react-redux-firebase'
After that you get the auth like this
const auth = useSelector( state => state.firebase.auth );
Then if the isLoaded( auth )
is not true (so the auth state is not loaded) you can redirect to a loading page or just return a <React.Fragment></React.Fragment>
if you don't want.
In the end if on your return, you can check if !isEmpty( auth )
, and if that is true return the children ponent, otherwise Redirect to the login page or any other page you prefer.