Search code examples
reactjsreact-reduxreact-router

Protected Route Keeps Redirecting to Login After Successful Login


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:

  1. Confirmed that isUserLogin state updates correctly after login.
  2. Ensured localStorage is set with the user information after login.
  3. Verified that the ProtectedPage component properly checks isUserLogin state. What could be causing the redirection to the login page when manually navigating to a protected route, and how can I resolve this issue?

Solution

  • 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;