How to authenticate user with Context API?

Viewed 97

I am having trouble logging in to my application using the Context API. When I run applications without having any token in my localStorage in the variable session I get a lot of errors like below:

Uncaught TypeError: Cannot read properties of null (reading 'name')

I think that this problem exists because my currentAccount from ApplicationContext is null.

dashboard/index.tsx

  const { currentAccount } = useContext(ApplicationContext);

  return (
      <span>{currentAccount.name}</span>
  );

On the routes.login login page I am also getting these exceptions even though this error should only be on routes.dashboard :/ Refreshing the page or clearing localStorage does not help. I;m having also an infinite loop over checkLogin in ApplicationContextProvider :(

login/index.tsx

  const { setCurrentAccount } = useContext(ApplicationContext);

  const onFinish = async (email: string; password: string) => {
      try {
        const response = await axios.post("/auth/login", {
            email: email,
            password: password
        });

        const token = response["token"];
        const account = response["account"];
        if (token && account) {
          localStorage.setItem("session", token);
          setCurrentAccount(account);

          history.push(routes.dashboard)
        }
      } catch (error) {

      }
  };

App.tsx

  return (
    <div className="App">
      <Switch>
        <ApplicationContextProvider>
          <Route path={route.login} component={Login} />
          <Main>
            <Route path={route.dashboard} component={Dashboard} />
          </Main>
        </ApplicationContextProvider>
      </Switch>
    </div>
  );

ApplicationContextProvider.tsx

export type AccountContext = {
  currentAccount?: Account;
  setCurrentAccount: (user: Account) => void;
  checkLogin: () => void;
};
export const ApplicationContext = React.createContext<AccountContext>(null);

interface ProviderProps {
  children: React.ReactNode
}

export const ApplicationContextProvider = ({ children }: ProviderProps) => {
  const [currentAccount, setCurrentAccount] = useState<Account>(null);

  useEffect(() => {
    checkLogin();
  }, [currentAccount]);

  const checkLogin = async () => {
    const token = localStorage.getItem("session");
    if (token) {
        const token = localStorage.getItem("session");
        const decode = jwt(token);
    
        const query = {
          id: decode["id"]
        }

        const response: Account = await api.get("/auth/account", query);
        setCurrentAccount(response);
    } else {
        setCurrentAccount(null);
    }
  };

  const stateValues = {
    currentAccount,
    setCurrentAccount,
    checkLogin
  };

  return (
    <ApplicationContext.Provider value={stateValues}>
      {children}
    </ApplicationContext.Provider>
  );

Can someone tell me what is wrong with my context logic to authentication user to application?

Thanks for any help!

1 Answers

I already know what's going on, maybe it will be useful to someone in the future.

I have to add at two important things. For the first useEffect() function need to have an empty dependency array like below:

  useEffect(() => {
    checkLogin();
  }, [ ]);

and for second I have to add also state to store loading like below:

const [loading, setLoading] = useState(false)

and set correct values when i've got data from api and so on

Related