Search code examples
reactjsreact-hooksuse-effect

How do I keep state persistant using local storage, react hooks, and Context Provider


I'm implementing authentication into my app, the issue I am having is on refresh the state is not persistent so I get redirected back to the login screen. How can I use React Hooks to check if there is an authToken in local storage to keep me logged in on refresh.

This is My Protected Route That checks for auth tokens

<Route
    {...rest}
    render={
        props=>
            authTokens ? (
                <Component {...props}/>
            ) : (
                <Redirect
                    to ={
                        {
                            pathname: '/login',
                            state: {
                                from: props.location
                            }
                        }
                    }
                />

This is my Login page Function

 function Login(props) {
const [isLoggedIn, setLoggedIn] = useState(false);
const [isError, setIsError] = useState(false);
const [firstName, setUserName] = useState("");
const [password, setPassword] = useState("");
const { setAuthTokens } = useAuth();

function postLogin() {
    axios.post("http://10.6.254.22:5000/userinformation/login", {
      firstName,
      password
    }).then(result => {
      if (result.status === 200) {
        console.log('You have been logged in as user: ' +result.data[0].firstName)
        setAuthTokens(result.data);
        setLoggedIn(true);
      } else {
        setIsError(true);
      }
    }).catch(e => {
      setIsError(true);
    });
  }


  if (isLoggedIn) {
    return <Redirect to="/" />;
  }

Below is my Hook that contains the state

    const [authTokens, setAuthTokens] = useState();

  const setTokens = (data) => {
    localStorage.setItem("authTokens", JSON.stringify(data));
    setAuthTokens(data);
  }

  useEffect(()=> {
    let jsonToken = localStorage.getItem('authTokens', JSON.stringify(authTokens))
    console.log('JSON TOKEN  -----' + jsonToken)
    setAuthTokens(jsonToken)
  })

How can i set the state authTokens with the local storage variable on load so that when I refresh the state isnt empty so i dont get logged out on page reload. Also to mention The context provider is set up as so:

<AuthContext.Provider value={{ authTokens, setAuthTokens: setTokens}}>
   <ProtectedRoute path="/" exact component={ProfileBox}/>
</AuthContext.Provider>

Solution

  • The problem is that authTokens is undefined when your component mounts, so the logic of your Protected route is evaluating to the Redirect portion of your code, causing the redirection to /login.

    React will remove componentWillMount hook, so, the solution I found to this specific problem, is to check also for the token in the localStorage, instead of just checking for the state authTokens.

    This way, your code will be able to evaluate that you have the token (even before the first component update).

    The code of your Protected Route may look like this:

    <Route
        {...rest}
        render={
            props=>
                (authTokens || localStorage.getItem("authTokens")) ? (
                    <Component {...props}/>
                ) : (
                    <Redirect
                        to ={
                            {
                                pathname: '/login',
                                state: {
                                    from: props.location
                                }
                            }
                        }
                    />
    

    Other possible solutions are:

    • Using Context
    • Using Redux

    But I think that if you just want to protect private routes, this solution is sufficient