0

I am making a simple SPA where you need to login before you can access other pages. I can successfully login and store the login data (firstname, lastname, etc.) cause I plan to use the data again later in the other pages. The problem is whenever I refresh the page, it always empty the state in the context which cause me to return to the login page. I am referring link for my SPA.

Do I need to do this? I would be thankful if someone can point out what I should change / improve. Thank you.

Here is my code.

App.js

import React, { useState } from "react";
import { BrowserRouter as Router, Link, Route } from "react-router-dom";

import { AuthContext } from "./context/auth";
import PrivateRoute from "./PrivateRoute";

import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Home from "./pages/Home";
import Admin from "./pages/Admin";

function App() {
  const [authTokens, setAuthTokens] = useState();

  const setTokens = (data) => {
    // console.log("DATA ",data);
    localStorage.setItem("tokens", JSON.stringify(data));
    setAuthTokens(data);
  }

  // console.log(authTokens);

  return (
    <AuthContext.Provider value={{ authTokens, setAuthTokens: setTokens }}>
      <Router>
        <div className="app">
          <ul>
            <li><Link to="/">Home Page</Link></li>
            <li><Link to="/admin">Admin Page</Link></li>
          </ul>
          <Route exact path="/login" component={Login} />
          <Route exact path="/signup" component={Signup} />
          <Route exact path="/" component={Home} />
          <PrivateRoute exact path="/admin" component={Admin} />
        </div>
      </Router>
    </AuthContext.Provider>
  );
}

export default App;
Login.js

import React, { useState } from "react";
import axios from "axios";
import { Link, Redirect } from "react-router-dom";

import { useAuth } from "../context/auth";

import { Card, Form, Input, Button, Error } from "../components/AuthForm";

const Login = () => {
  const [isLoggedIn, setLoggedIn] = useState(false);
  const [isError, setIsError] = useState(false);
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const { setAuthTokens } = useAuth();

  const handleLogin = () => {
    axios
      .post("LOGINLINK", {
        email,
        password,
      })
      .then((result) => {
        if (result.status === 200) {
          setAuthTokens(result.data);
          setLoggedIn(true);
        } else {
          setIsError(true);
        }
      })
      .catch((error) => {
        setIsError(true);
      });
  };

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

  return (
    <Card>
      <Form>
        <Input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => {
            setEmail(e.target.value);
          }}
        />
        <Input
          type="password"
          placeholder="password"
          value={password}
          onChange={(e) => {
            setPassword(e.target.value);
          }}
        />
        <Button onClick={handleLogin}>Login</Button>
      </Form>
      <Link to="/signup">Don't have an account?</Link>
      {isError && (
        <Error>The username or password provided were incorrect!</Error>
      )}
    </Card>
  );
};

export default Login;

Auth.js

import { createContext, useContext } from "react";

export const AuthContext = createContext();

export function useAuth() {
  console.log("CONTEXT", useContext(AuthContext));
  return useContext(AuthContext);
}
ayoussef
  • 13
  • 1
  • 5

2 Answers2

1

In your App component you need to fetch the data from localStorage when initializing your state so it has some data to start with.

const localToken = JSON.parse(localStorage.getItem("tokens"));
const [authTokens, setAuthTokens] = useState(localToken);

If user has already authenticated it will be available in localStorage else it's going to be null.

Jack
  • 788
  • 3
  • 13
  • Hey thanks for your response. It works but then I got an error when I try to logout. It says "A cross-origin error was thrown. React doesn't have access to the actual error object in development." Does it have to do with my backend? Or I can fix it from my frontend? – ayoussef Sep 14 '21 at 04:55
  • You get CORS error if your react app is hosted at one domain and your backend is at different domain so what are you using for backend is it node and express ? – Jack Sep 14 '21 at 06:10
  • yup, I am using node and express – ayoussef Sep 14 '21 at 06:11
  • Then install this library https://www.npmjs.com/package/cors and use that middleware with your app this will solve the error and read about CORS and what is it so you can get an understanding about that – Jack Sep 14 '21 at 06:13
  • Okay turns out that there's already CORS middleware. I cleared the localstorage for the logout and its now working well. Previously when I logout there is still "tokens" with undefined value. Now its just an empty localstorage. – ayoussef Sep 14 '21 at 06:25
0

I also had same problem but I solved liked this Don't use localStorage directly use your state and if it is undefined then only use localStorage. cause directly manipulating state with localStorage is in contrast with react internal state and effects re-render .

const getToken = () => {
  JSON.parse(localStorage.getItem('yourtoken') || '')
} 
const setToken = (token) => {
  localStorage.setItem('key' , token)
}
  const [authTokens, setAuthTokens] = useState(getToken());

  const setTokens = (data) => {
    // console.log("DATA ",data);
    setToken(token);
    setAuthTokens(data);
  }
Build Though
  • 300
  • 3
  • 9