Search code examples
reactjsreact-hooksreact-reduxrtk-query

How to avoid redirect from logged in page to home(login page) by changing the url manually-react


Say the user is already logged in and redirected to home page again if he modifies the url to navigate to login page it should not do go back to login.

Login.js

const [loginUser, { data, isLoading, isError }] = useLoginUserMutation();
useEffect(() => {
if (data?.response === "true" && data?.state === "success"){
setErrorMsg("");
setEmail("");
setPassword("");
setIsAuthenticated(true);
navigate("/home", { state: { user: data?.user } });
}
else if (data?.response === "false" && data.state === "error"){
  setErrorMsg(true);
}
  else{
    setErrorMsg(false)
  }
}, [data,isError,isLoading]);

const handleLogin = async (e) => {
e.preventDefault();
console.log("****")
await loginUser({email,password})

}

App.js

function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const handleLogin = () => {
setIsAuthenticated(true);
}

return (
<div className="App">
<Router>
<Routes>
  <Route exact path="/">
    <Login onLogin={handleLogin} />
  </Route>
  <ProtectedRoute path="/home" component={Home} isAuthenticated= 
 {isAuthenticated}/>
</Routes>
</Router>
</div>
);
}
export default App;

protectedRoute.js

import React from "react";
import { Navigate,Outlet } from "react-router-dom";

const ProtectedRoute = ( {isAuthenticated}) => {
return isAuthenticated? <Outlet /> : <Navigate to="/" />;
}

export default ProtectedRoute;

UserApi

import{createApi,fetchBaseQuery} from'@reduxjs/toolkit/query/react'

export const authApi = createApi({
reducerPath: 'authApi',
baseQuery: fetchBaseQuery({ baseUrl: "http://localhost:8100" }),
endpoints: (builder) => ({
  loginUser: builder.mutation({
    query: (body) => {
      return {
        url: "users/authenticate",
        method: "post",
        body,
      };
    },
    transformResponse: (response) => {
      const { response: resp, state, user } = response;
      return { response: resp, state: state, user: user };
    },
  }),
}),
});

export const { useLoginUserMutation } = authApi;
enter code here

I tried persisting the state Auth with local storage, since data wont be available it shows undefined/null.

const isAuthenticated = localStorage.getItem('data');

if (isAuthenticated) {
setAuthenticationStatus(true);

since I am using Redux Toolkit I am not sure if I have to use localStorage to persist the data.

Any solution would be appreciated

first render localhost:3000(loginPage) After successfull LoggedIn localhost:3000/home Manually i change the URL: localhost:3000/ it comes back to login page


Solution

  • The main issue is that the isAuthenticated state isn't persisted to any longterm storage and ins't initialized from longterm storage when the app mounts.

    The easier short-term solution is to use the isAuthenticated state that's declared in App and pass isAuthenticated state and updater function down as props to the routed components that need it.

    To combat the issue of "authenticated" users later accessing the "/login" route the answer is to create another route protection component that applies the inverse of the ProtectedRoutes component, e.g. authenticated users are bounced off the route.

    Route protectors:

    import React from "react";
    import { Navigate, Outlet } from "react-router-dom";
    
    // Unauthenticated users redirected to log in route
    const ProtectedRoute = ({ isAuthenticated }) => {
      return isAuthenticated ? <Outlet /> : <Navigate to="/login" replace />;
    };
    
    // Authenticated users redirected to safe route
    const AnonymousRoute = ({ isAuthenticated }) => {
      return isAuthenticated ? <Navigate to="/" replace /> : <Outlet />;
    };
    
    function App() {
      // Initialize state from localStorage
      const [isAuthenticated, setIsAuthenticated] = useState(() => {
        return JSON.parse(localStorage.getItem("auth")) ?? false;
      });
    
      // Side-effect to persist state changes to localStorage
      useEffect(() => {
        localStorage.setItem("auth", JSON.stringify(isAuthenticated));
      }, [isAuthenticated]);
    
      const handleLogin = () => {
        setIsAuthenticated(true);
      };
    
      return (
        <div className="App">
          <BrowserRouter>
            <Routes>
              <Route path="/" element={<Navigate to="/home" replace />} />
    
              <Route element={<AnonymousRoute isAuthenticated={isAuthenticated} />}>
                <Route path="/login" element={<Login onLogin={handleLogin} />} />
                {/* ... other "anonymous" routes ... */}
              </Route>
    
              <Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
                <Route path="/home" element={<Home />} />
                {/* ... other "authenticated" routes ... */}
              </Route>
            </Routes>
          </BrowserRouter>
        </div>
      );
    }
    export default App;
    
    const Login = ({ onLogin }) => {
      const navigate = useNavigate();
      const [loginUser, { data, isLoading, isError }] = useLoginUserMutation();
      ...
    
      const handleLogin = async (e) => {
        e.preventDefault();
    
        setErrorMsg("");
        try {
          // Call mutation trigger and await and unwrap resolved response
          await loginUser({ email, password }).unwrap();
    
          // Update the auth state
          onLogin();
    
          // Redirect back to home
          navigate("/home", { replace: true });
        } catch(error) {
          setErrorMsg(true);
        }
      };
    
      ...
    };