1

I have some protected routers but I need to maintain the state on page refresh.

import React from 'react'
import { Route, Redirect, Switch } from 'react-router-dom'

const auth = {
    isAuthenticated: true // this would be an http call to get user data
}

const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route {...rest} render={(props) => (
        auth.isAuthenticated === true
        ? <Component {...props} />
        : <Redirect to="/login" />
    )} />
)

const Main = () => {

    return (
        <Switch>
            <Route path="/login" exact strict component={Login}/>
            <Route path="/logout" exact strict component={Logout}/>
            <PrivateRoute path="/profile" exact strict component={Profile}/>
        </Switch>
    )
}

export default Main

where should I make the service call? in the Main app? in a context?

update: I added a check in Main that makes a call to the api sending the token that I have stored. that call could return 200 with data or 401

const auth = {
    isAuthenticated: Api.status() // this is just a fetch to /status endpoint 
        .then(
            status => {
                console.log(status);
                return true;
            },
            error => {
                console.log(error);
                return false;
            }
        )
}

but when i hit /profile it redirects me immediately to login (because isAuthenticated is false)

my question is based entirely on the scenario where the user refresh the page (F5) other scenarios are working fine. sorry If I´m not clear enough happy to clarify anything thanks again!

handsome
  • 2,335
  • 7
  • 45
  • 73
  • You might want to use `localStorage` or `IndexedDB` to store state when it changes and restore it from it when you reload the page. – Sergei Basharov Nov 15 '19 at 15:17
  • that could possibly generate another issue. like checking for a token that´s expired. I also need to make the service call to get the user details (name, email, etc) that should persist across all pages of the app. – handsome Nov 15 '19 at 15:19
  • Does this answer your question? [On React Router, how to stay logged in state even refresh the page?](https://stackoverflow.com/questions/39097440/on-react-router-how-to-stay-logged-in-state-even-refresh-the-page) – Jurrian Nov 15 '19 at 15:19
  • hi @Jurrian actually thats the problem I mentioned before about storing an expired jwt, that could be solved making a call passing that token and if the call fails then remove the token. but that´s pretty much my question. where should I make this http call that needs to be executed on each page refresh – handsome Nov 15 '19 at 15:23
  • 1
    it's a bit a misleading question to the title.. if you're going to retrieve the state with an http call when the component loads then you don't need to store it. Thus, your question seems more like you're asking whether a.) it's a good practice to make that call each time, or b) a conditional alternative and means of storing the state. But your code doesn't reflect the initial attempt. What have you tried? – Brett Caswell Nov 15 '19 at 15:24

3 Answers3

3

the best way in my opinion is the local storage save the token in there dispatch an action in componentDidMount() in the root component of you app and store the token in readucer state

Arghya Sadhu
  • 41,002
  • 9
  • 78
  • 107
Amir Hossein
  • 133
  • 5
3

You can implement an auth-context like shown here: https://usehooks.com/useAuth/

And in addition store user in localStorage (or storage of your preference) and when the auth-context mounts read from localStorage and set the initial value like so:

const [user, setUser] = useState(JSON.parse(localStorage.getItem('user')));

This way the protected routes will automatically redirect (or whatever they're supposed to do in an authenticated state) when reloading the page.

marjon4
  • 332
  • 3
  • 9
-1

In this case I'd make an AuthContext component. By using the context provider, you can access the user logged in state everywhere in your app.

import { createContext } from 'react';

const AuthContext = createContext({
  token: null,
  userId: null,
  login: () => {},
  logout: () => {}
});

export default AuthContext;

And provide this at the highest level in your app.

import React, { useState } from 'react';
import { Route, Switch } from 'react-router-dom';
import AuthContext from './context/AuthContext';

function App() {
  const [token, setToken] = useState(null);
  const [userId, setUserId] = useState(null);

  const login = (token, userId) => {
    setToken(token);
    setUserId(userId);
  };

  const logout = () => {
    setToken(null);
    setUserId(null);
  };

  return (
     <Switch>
        <Route path="/login" exact strict component={Login}/>
        <Route path="/logout" exact strict component={Logout}/>
        <PrivateRoute path="/profile" exact strict component={Profile}/>
     </Switch>
  )
}

export default App;

And in a login/logout form you make the call to a database or localstorage.

import React, { useContext } from 'react';
import AuthContext from '../context/AuthContext';

function Auth() {
  const { login } = useContext(AuthContext);

  const submitHandler = () => {
    // Make the call here
  };

  return (
    <Form onSubmit={submitHandler}>
     // Put your login form here
    </Form>
  );
}

export default Auth;
Jurrian
  • 850
  • 6
  • 20
  • what happen when you reload the /profile page? from what I see the context will just return null for token and userId. because the call that generates the token you store is only being made on /login – handsome Nov 15 '19 at 15:39
  • You should save the token somewhere. You can do this locally or on the server. So that when a request is being made to the server you can validate the token. – Jurrian Nov 15 '19 at 15:44
  • I think you should watch a tutorial about this. I'm a big fan of Maximilian Schwarzmuller & WesBos, so check them out. If you have a specific problem, then ask a question here. – Jurrian Nov 15 '19 at 15:47
  • Seems like you could use some theory – Jurrian Nov 15 '19 at 15:52
  • hmm.. yea, using a context is a good recommendation, but not really applicable. The part of this answer that is applicable could be improved.. The React Hooks may not need explaining (but may be worth a reference) but the concept of getting the state back into the `AuthContext` isn't entirely clear (is it automatic?). Also, you could include an example of checking a property in `AuthContext` (with the state loaded). – Brett Caswell Nov 15 '19 at 16:17
  • So what is exactly your question? – Jurrian Nov 15 '19 at 16:21
  • considering you answer, I think what the OP wants to know is - if after he logs in (and stores the state in this manner), and navigates to the profile page\route.. if he refreshes the page (F5), will the data in `AuthContext` persist without additional steps?.. – Brett Caswell Nov 15 '19 at 16:28
  • yes that´s exactly my question. when I reload the page if im signed in im kicked out to login page again because isAuthenticated is false (question updated with this) – handsome Nov 15 '19 at 16:58
  • Just store a jwt t in localStorage or sessionStorage – Jurrian Nov 16 '19 at 19:59