React - 如何检查用户是否仍然通过身份验证

React - How to check if user is still authenticated


我有一个反应中的小应用程序,我需要检查用户离开页面后是否仍然通过身份验证。但是我的解决方案不起作用。
我认为我在 useEffect 部分做了一些不好的事情。因为我在第一次渲染时没有用户。
这是一个好的解决方案还是可以用 better/different 方式完成?

感谢您的任何建议。

登录页面 - 处理登录

  const handleLogin = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    try {
      if (!username || !password) {
        setError('Pole musí být vyplněna!');
        return;
      }

      const userData = await axios.post(
        `${process.env.REACT_APP_BACKEND_URL}/account/login`,
        {
          username,
          password,
        }
      );

      if (!userData) {
        throw new Error();
      }

      setUser(userData.data);
      localStorage.setItem('token', userData.data.token);
      navigate(state?.path || '/home');
    } catch (err: any) {
      setError('Nesprávné jméno nebo heslo!');
    }
  };

App.ts

function App() {
  const [user, setUser] = useState<ICurrentUser>({
    name: '',
    role: '',
    username: '',
  });

  useEffect(() => {
    const checkUser = async () => {
      const token = localStorage.getItem('token');
      if (token !== null) {
        const userData = await axios.get(
          `${process.env.REACT_APP_BACKEND_URL}/account/checkToken`,
          {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          }
        );

        setUser({
          name: `${userData.data.firstname} ${userData.data.lastname}`,
          role: userData.data.role,
          username: userData.data.username,
        });
      }
    };

    checkUser().catch((err) => console.log(err));
  }, []);

  return (
    <>
      <UserContext.Provider value={{ user, setUser }}>
        <Routes>
          <Route path="/" element={<Login />}></Route>
          <Route
            path="/home"
            element={
              <ProtectedRoute>
                <HomePage />
              </ProtectedRoute>
            }
          />
          <Route
            path="stazky"
            element={
              <ProtectedRoute>
                <Stazky />
              </ProtectedRoute>
            }
          />
          <Route
            path="zamestnanci"
            element={
              <ProtectedRoute>
                <Zamestnanci />
              </ProtectedRoute>
            }
          />
          <Route
            path="newZam"
            element={
              <ProtectedRoute>
                <NovyZamestnanec action="create" />
              </ProtectedRoute>
            }
          />
          <Route
            path="zam/:id"
            element={
              <ProtectedRoute>
                <NovyZamestnanec action="edit" />
              </ProtectedRoute>
            }
          />
          <Route
            path="new"
            element={
              <ProtectedRoute>
                <NovaStazka />
              </ProtectedRoute>
            }
          />
          <Route
            path="vozidla"
            element={
              <ProtectedRoute>
                <Vozidla />
              </ProtectedRoute>
            }
          />
          <Route
            path="newVehicle"
            element={
              <ProtectedRoute>
                <NoveVozidlo />
              </ProtectedRoute>
            }
          />
          <Route
            path="*"
            element={
              <ProtectedRoute>
                <NotFound />
              </ProtectedRoute>
            }
          />
        </Routes>
      </UserContext.Provider>
    </>
  );
}

ProtectedRoute.ts

const ProtectedRoute: React.FC<IProps> = ({ children }) => {
  const location = useLocation();
  const { user } = useContext(UserContext);
  const isAuth = !!user.username;

  return isAuth ? (
    <Layout>{children}</Layout>
  ) : (
    <Navigate to="/" replace state={{ path: location.pathname }} />
  );
};

export default ProtectedRoute;

我终于知道怎么做了。

唯一需要做的就是将“加载状态”添加到上下文中。 我会 post 我的代码以供参考,如果其他人正在为此苦苦挣扎。

受保护的路线

const ProtectedRoute: React.FC<IProps> = ({ children }) => {
  const location = useLocation();
  const { user, loading } = useContext(UserContext);
  const isAuth = !!user.username;

  if (loading) {
    return <h1>Loading..</h1>;
  }

  return isAuth ? (
    <Layout>{children}</Layout>
  ) : (
    <Navigate to="/" replace state={{ path: location.pathname }} />
  );
};

App.js

function App() {
  const [user, setUser] = useState<ICurrentUser>({
    name: '',
    role: '',
    username: '',
  });
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkUser = async () => {
      const token = localStorage.getItem('token');
      if (token !== null) {
        const userData = await axios.get(
          `${process.env.REACT_APP_BACKEND_URL}/account/checkToken`,
          {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          }
        );

        localStorage.setItem('token', userData.data.token);
        setUser({
          name: `${userData.data.firstname} ${userData.data.lastname}`,
          role: userData.data.role,
          username: userData.data.username,
        });

        setLoading(false);
      }
    };

    checkUser();
  }, []);

  return (
    <>
      <UserContext.Provider value={{ user, setUser, loading }}>
        <Routes>
          <Route path="/" element={<Login />}></Route>
          <Route
            path="/home"
            element={
              <ProtectedRoute>
                <HomePage />
              </ProtectedRoute>
            }
          />
          <Route
            path="stazky"
            element={
              <ProtectedRoute>
                <Stazky />
              </ProtectedRoute>
            }
          />
                   </Routes>
      </UserContext.Provider>
    </>
  );
}

上下文

interface IContent {
  loading: boolean;
  user: ICurrentUser;
  setUser: (user: ICurrentUser) => void;
}

export const UserContext = createContext<IContent>({
  user: {
    name: '',
    role: '',
    username: '',
  },
  setUser: () => {},
  loading: true,
});