Skip to content

Instantly share code, notes, and snippets.

@sidsbrmnn
Last active September 18, 2020 08:32
Show Gist options
  • Save sidsbrmnn/cdd6cc41ef155ca810b37da9caea682b to your computer and use it in GitHub Desktop.
Save sidsbrmnn/cdd6cc41ef155ca810b37da9caea682b to your computer and use it in GitHub Desktop.
Protected route using Redux + React Router
import React, { FunctionComponent, useEffect, Suspense } from 'react';
import { useDispatch } from 'react-redux';
import { Switch, Route } from 'react-router-dom';
import { Dashboard, History, Landing } from '../pages';
import { fetchUser } from '../store/auth/thunks';
import ProtectedRoute from './protected-route';
const App: FunctionComponent = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchUser());
}, []);
return (
<div className="font-sans antialiased">
<Suspense
fallback={
<div className="flex items-center justify-center min-h-screen">
Loading...
</div>
}
>
<Switch>
<Route path="/" exact component={Landing} />
<ProtectedRoute path="/dashboard" component={Dashboard} />
<ProtectedRoute path="/history" component={History} />
</Switch>
</Suspense>
</div>
);
};
export default App;
import React, { FunctionComponent } from 'react';
import { useSelector } from 'react-redux';
import { Redirect, Route, RouteProps } from 'react-router-dom';
import { RootState } from '../store';
import Loading from './loading';
import Layout from './layout';
type Props = RouteProps;
const ProtectedRoute: FunctionComponent<Props> = ({
component: Component,
...rest
}: Props) => {
const user = useSelector((state: RootState) => state.auth.user);
if (!Component) {
return <Redirect to="/" />;
}
switch (user) {
case null:
return <Loading />;
case false:
return <Redirect to="/" />;
default:
return (
<Route
{...rest}
render={(props) => (
<Layout user={user}>
<Component {...props} />
</Layout>
)}
/>
);
}
};
export default ProtectedRoute;
import { AuthState, AuthActionTypes, SET_USER } from './types';
const initialState: AuthState = {
user: null,
};
export function authReducer(
state = initialState,
action: AuthActionTypes
): AuthState {
switch (action.type) {
case SET_USER:
return {
...state,
user: action.payload || false,
};
default:
return state;
}
}
import { applyMiddleware, combineReducers, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { authReducer } from './auth/reducers';
const rootReducer = combineReducers({
auth: authReducer,
});
export type RootState = ReturnType<typeof rootReducer>;
const store = createStore(rootReducer, {}, applyMiddleware(thunkMiddleware));
export default store;
import axios from 'axios';
import { ActionCreator } from 'redux';
import { ThunkAction } from 'redux-thunk';
import { setUser } from './actions';
import { AuthActionTypes, AuthState, User } from './types';
export const fetchUser: ActionCreator<ThunkAction<
Promise<void>,
AuthState,
null,
AuthActionTypes
>> = () => async (dispatch) => {
try {
const res = await axios.get<User>('/api/users/me', {
withCredentials: true,
});
dispatch(setUser(res.data));
} catch (error) {
dispatch(setUser());
}
};
export const SET_USER = 'SET_USER';
export interface User {
_id: string;
googleId: string;
fullName: string;
email: string;
avatarUrl?: string;
}
interface SetUserAction {
type: typeof SET_USER;
payload?: User;
}
export interface AuthState {
user: User | null | false;
}
export type AuthActionTypes = SetUserAction;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment