简体   繁体   中英

React Private Route is redirecting before logging in on refresh

I am creating a webpage with a few private routes. If the user is not logged in, I want to redirect them to the home page. If the user is logged in, I want to display the page they were originally trying to access. The set up I have currently is working for that intended purpose, but whenever a person is on the Private route who is logged in, and then refreshes the page, the private route redirects them to the home page. Unfortunately, my loggedInProvider is running it's logged in code AFTER the private route checks the loggedIn boolean and redirects them. SO technically on refresh it is not logging the person in quick enough. Is there a way to wait for the logged in useEffect to run before running the private route code?

Here is the useEffect code in my loggedInContext:

import { useHistory } from "react-router-dom";
import React, { createContext, useState, useEffect } from "react";
import Cookie from "js-cookie";
import axios from "axios";

export const LoggedInContext = createContext();

export function LoggedInProvider(props) {
  const history = useHistory();
  const [loggedIn, setLoggedIn] = useState(false);
  const [token, setToken] = useState("");
  const [loading, setLoading] = useState();
  const [userData, setUserData] = useState({});
  //use loading variable to make splash screen - just export and write an if statement to show splashscreen if loading is true in top level app



const signUserIn = (token, userData, path = undefined) => {
    Cookie.set("token", token);
    changeLogIn(true);
    setUserData(userData.user);
    NavigateAway(path);
  };

  useEffect(() => {
    console.log("UseEffect from LoggedIn");
    setLoading(true);
    const jwt = Cookie.get("token");
    const fetchData = async () => {
      try {
        const config = {
          method: "POST",
          url: "https://prothesist-backend.herokuapp.com/api/v1/user/sign-in/token",
          headers: {
            "Content-Type": "application/json",
          },
          data: {
            token: jwt,
          },
        };
        const { data } = await axios(config);
        setLoggedIn(true);
        // setUserData(data.user);
        signUserIn(jwt, data);
        setLoading(false);
        //use userdata to fill in data
      } catch (err) {
        console.log(err);
      }
    };
    if (jwt) {
      fetchData();
    } else {
      setLoggedIn(false);
      setLoading(false);
    }
  }, []);

  const updateUserData = (data) => {
    setUserData(data);
  };
  const changeLogIn = (val) => {
    setLoggedIn(val);
    if (!val) NavigateAway("/");
  };

  const handleSignOut = () => {
    console.log("signedout");

    var myHeaders = new Headers();
    myHeaders.append("Cookie", "jwt=loggedout");

    var requestOptions = {
      method: "GET",
      headers: myHeaders,
      redirect: "follow",
    };

    fetch(
      "https://prothesist-backend.herokuapp.com/api/v1/user/logout",
      requestOptions
    )
      .then((response) => response.text())
      .then((result) => {
        setLoggedIn(false);
        Cookie.set("token", "");
        console.log(result);
      })
      .catch((error) => console.log("error", error));
  };

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

  const NavigateAway = (path) => {
    history.push(path);
  };

  return (
    <LoggedInContext.Provider
      value={{
        userData,
        setUserData: setUserData,
        loading,
        loggedIn,
        changeLogIn,
        handleSignOut,
        token,
        setToken: setTokens,
      }}
    >
      {props.children}
    </LoggedInContext.Provider>
  );
}

Here is my Private route:

import React, { useContext, useEffect } from "react";
import { Redirect, Route } from "react-router-dom";
import { LoggedInContext } from "contexts/LoggedIn.js";

export default function PrivateRoute({ children, ...rest }) {
  const { loggedIn } = useContext(LoggedInContext);
  return (
    <Route
      {...rest}
      render={({ location }) =>
        loggedIn ? (
          children
        ) : (
          <Redirect to={{ pathname: "/", state: { registerModal: true } }} />
        )
      }
    />
  );
}

I would think you would just add the loading check:

export default function PrivateRoute({ children, ...rest }) {
  const { loggedIn, loading } = useContext(LoggedInContext);
  return (
    <Route
      {...rest}
      render={({ location }) => {
        if (loggedIn) return children;
        else if (loading) return <Spinner />;
        else return (
          <Redirect to={{ pathname: "/", state: { registerModal: true } }} />
        );
      }}
    />
  );
}

and then possibly default loading to true in your provider

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM