I'm implementing protected routes in my React application, using Redux for state management and React Router for routing. After logging in successfully, I'm able to navigate to protected routes. However, when I manually paste the url and try to navigate to a protected route (e.g., video/1245/), I'm redirected back to the login page even though I'm logged in.
Login Component:
function Login() {
const navigate = useNavigate();
const dispatch = useDispatch();
const formik = useFormik<formValues>({
initialValues: {
email: '',
password: ''
},
validationSchema: loginValidation,
onSubmit: () => {
axios.post('/login/', {
email: formik.values.email,
password: formik.values.password
}, {
headers: {
'Content-Type': 'application/json'
}
})
.then((response: AxiosResponse) => {
if (response.status === 200) {
toast.success('Login successful');
navigate('/');
dispatch(loginUser(response.data.userStatus));
try {
localStorage.setItem('userInfo', JSON.stringify(response.data));
} catch (error) {
toast.error('Something went wrong');
}
}
})
.catch((error: AxiosError) => {
toast.error(error.response?.data as string);
});
}
});
return (
);
}
export default Login;
Protected Page Component:
const ProtectedPage: React.FC<ProtectedPageProps> = ({ Component }: ProtectedPageProps) => {
const navigate = useNavigate();
const { isUserLogin } = useSelector((state: RootState) => state.userAuth);
useEffect(() => {
if (!isUserLogin) {
navigate('/login');
}
}, [isUserLogin]);
return <>{isUserLogin ? Component : null}</>;
};
export default ProtectedPage;
User Auth Slice
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
export const userAuthSlice = createSlice({
name: 'userAuth',
initialState: {
isUserLogin: false,
userStatus: ''
},
reducers: {
loginUser: (state, action: PayloadAction<string>) => {
state.isUserLogin = true;
state.userStatus = action.payload;
},
logoutUser: (state) => {
localStorage.clear();
state.isUserLogin = false;
state.userStatus = '';
}
}
});
export const { loginUser, logoutUser } = userAuthSlice.actions;
export default userAuthSlice.reducer;
App Component
function App() {
return (
<>
<Navbar />
<ToastContainer />
<Routes>
<Route path='/' element={<Landing />} />
<Route path='/login' element={<Login />} />
<Route path='/signup' element={<Signup />} />
<Route path='/verification' element={<Verification />} />
<Route path='/verification-request' element={<VerificationRequest />} />
<Route path='/password-reset-request' element={<PasswordResetRequest />} />
<Route path='/reset-password' element={<ResetPassword />} />
<Route path='/password-reset-verify' element={<PasswordResetVerify />} />
<Route path='/upload-video' element={<UploadVideo />} />
<Route path='/videos/' element={<ProtectedPage Component={<Videos />} />} />
<Route path='/video/:videoId' element={<ProtectedPage Component={<RetriveVideo />} />} />
</Routes>
</>
);
}
export default App;
Issue: When I navigate to a protected route manually after logging in, I'm redirected to the login page instead of accessing the protected route.
What I've tried:
The problem is redux state is non persistent, if you manually load a protected page it will read the initial state that you configured while creating slice.
So the solution would be using localStorage
instead of just false
in isUserLogin
property. Now only after login localStorage
will have that value so validation will be persistent across all tabs.
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
export const userAuthSlice = createSlice({
name: 'userAuth',
initialState: {
isUserLogin: Boolean(window.localStorage.getItem('userInfo')), //HERE
userStatus: ''
},
reducers: {
loginUser: (state, action: PayloadAction<string>) => {
state.isUserLogin = true;
state.userStatus = action.payload;
},
logoutUser: (state) => {
localStorage.clear();
state.isUserLogin = false;
state.userStatus = '';
}
}
});
export const { loginUser, logoutUser } = userAuthSlice.actions;
export default userAuthSlice.reducer;