在我的受保护路线上使用插座时显示黑色页面

问题描述 投票:0回答:1

我正在使用 React 并更新了我的应用程序以使用整体布局。

有公共路线和保护路线。

当我将代码从使用子项更改为

<Outlet>
时,当我导航到任何受保护的路线时,我会看到一个黑色页面。

我已经调试了一会儿,但我无法弄清楚。

它将如何“加载受保护的路线”......非常快,然后变黑。

有什么想法吗?

我的理解是我需要使用

<Outlet>
而不是
children

我什至尝试只返回

return <Outlet/>;
,但它仍然显示黑色。

这是我的设置:

import Admin from "./restricted/Admin";

import { AuthProvider, Logout, APIProvider, ToastProvider, ErrorPage, ErrorBoundaryRouter, ProtectedRoute } from "@app/Shared"
import { Route, Routes } from "react-router-dom";

import HomePage from "./home/Home";
import Layout from "./layouts/Layout";

function App() {
  return (
    <ErrorBoundaryRouter fallbackUrl="/ErrorPage">
    <AuthProvider>
      <APIProvider>
        <ToastProvider>
        <Routes>
          {/* All routes share the Layout */}
          <Route element={<Layout />}>
              {/* Public Routes */}
              <Route path="/" element={<HomePage />} />
              <Route path="/public" element={<Public/>} />
            {/* Error Route */}
            <Route path="/Error/:message" element={<ErrorPage />} />
            
            {/* Protected Routes */}
            <Route element={<ProtectedRoute/>}>
              <Route path="/admin" element={<Admin />} />
              <Route path="/userUpdate" element={<userUpdate />} />
              <Route path="/logout" element={<Logout />} />
            </Route> 
          </Route>
        </Routes>
        </ToastProvider>
      </APIProvider>
    </AuthProvider>  
    </ErrorBoundaryRouter>  
  );
}
export default App;

这是受保护的路线:

import { useEffect, useState } from "react";
import { useAuth } from "./useAuth";
import { userServer } from "../api/userServer";
import { Outlet } from "react-router-dom";


export const ProtectedRoute = () => {
  const { user, fetchUserClaims } = useAuth();
  const userAPI = userServer();
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkAuthentication = async () => {
      try {
        const isLoggedIn = await userAPI.getUserLoggedInStatus();
        if (!isLoggedIn) {
          window.location.href = "/"; // Redirect to home if not logged in
        } else if (!user) {
          await fetchUserClaims();
        }
      } catch (error) {
        console.error("Error during authentication check:", error);
        window.location.href = "/"; // Redirect to home on error
      } finally {
        setLoading(false);
      }
    };

    checkAuthentication();
  }, [user]);

  if (loading) {
    return <div>Loading Protected Content...</div>;
  }

  return user && user.isLoggedIn ? <Outlet/>: null;
};

export default ProtectedRoute;

这是我的布局:

import React, { useEffect, useState } from "react";
import { Outlet } from "react-router-dom";
import { TopToolbar, useAuth, isAuthenticated } from "@app/shared";
import { CardItem, getCardItems } from "../home/cardItems";

const Layout: React.FC = () => {
  const { logout, fetchUserClaims } = useAuth();

  const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
  const [userData, setUserData] = useState<{ firstName: string; lastName: string } | null>(null);
  const [initials, setInitials] = useState<string>("?");
  const [cardItems, setCardItems] = useState<CardItem[]>([]);

  // Helper function to calculate initials
  const calculateInitials = (firstName?: string, lastName?: string) => {
    if (firstName && lastName) {
      return `${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase();
    }
    return "?";
  };

  // Fetch user data and determine card items
  const checkAuthentication = async () => {
    try {
      const loggedInStatus = await isAuthenticated();
      setIsLoggedIn(loggedInStatus);

      if (loggedInStatus) {
        const claims = await fetchUserClaims();
        if (claims) {
          setUserData({ firstName: claims.firstName, lastName: claims.lastName });
          setInitials(calculateInitials(claims.firstName, claims.lastName));
          setCardItems(getCardItems(true)); // Get logged-in card items
        }
      } else {
        setUserData(null);
        setInitials("?");
        setCardItems(getCardItems(false)); // Get guest card items
      }
    } catch (error) {
      setIsLoggedIn(false);
      setUserData(null);
      setInitials("?");
      setCardItems(getCardItems(false)); // Fallback for unauthenticated state
    }
  };

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

  const handleLogout = () => {
    logout();
    setIsLoggedIn(false);
    setUserData(null);
    setInitials("?");
    setCardItems(getCardItems(false));
  };

  return (
    <div className="h-screen flex flex-col">
      {/* Top Toolbar */}
      <div>
        <TopToolbar
          userInitials={initials}
          firstName={userData?.firstName || "Guest"}
          lastName={userData?.lastName || ""}
          onLogout={isLoggedIn ? handleLogout : () => {}}
          allowToggleMenu={isLoggedIn}
        />
      </div>

      {/* Main Content */}
      <main className="flex-grow overflow-auto">
        {/* Provide cardItems via Outlet context */}
        <Outlet context={{ cardItems }} />
      </main>
    </div>
  );
};

export default Layout;

主要内容如下:

import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom';
import { registerLicense } from '@syncfusion/ej2-base'

registerLicense(window._env_.VITE_SYNCFUSION_LICENSE_KEY)

ReactDOM.createRoot(document.getElementById('root')!).render(
    <BrowserRouter>
      <App />
    </BrowserRouter>
)

使用应用程序package.json

"dependencies": {
    "@syncfusion/ej2": "~27.2.2",
    "@syncfusion/ej2-base": "~27.2.2",
    "@syncfusion/ej2-data": "~27.2.2",
    "@syncfusion/ej2-react-buttons": "~27.2.2",
    "@syncfusion/ej2-react-dropdowns": "~26.1.38",
    "@syncfusion/ej2-react-grids": "~27.2.2",
    "@syncfusion/ej2-react-inputs": "~27.2.2",
    "@syncfusion/ej2-react-layouts": "~27.2.2",
    "@syncfusion/ej2-react-navigations": "~27.2.2",
    "@syncfusion/ej2-react-notifications": "~27.2.2",
    "@syncfusion/ej2-react-pdfviewer": "~27.2.3",
    "@syncfusion/ej2-react-popups": "~27.2.2",
    "@syncfusion/ej2-react-querybuilder": "~27.2.2",
    "dotenv": "~16.4.5",
    "immer": "~10.0.4",
    "react": "~18.2.0",
    "react-dom": "~18.2.0",
    "react-router-dom": "~7.0.1",
    "remark-rehype": "^11.1.1",
    "use-immer": "~0.9.0",
    "uuid": "~9.0.1",
    "zustand": "~4.5.2"
  },
  "devDependencies": {
    "@types/node": "~20.12.7",
    "@types/react": "~18.2.79",
    "@types/react-dom": "~18.2.25",
    "@types/uuid": "~9.0.8",
    "@typescript-eslint/eslint-plugin": "~7.7.1",
    "@typescript-eslint/parser": "~7.7.1",
    "@vitejs/plugin-basic-ssl": "~1.1.0",
    "@vitejs/plugin-react": "~4.3.3",
    "@vitejs/plugin-react-swc": "~3.6.0",
    "autoprefixer": "~10.4.19",
    "concurrently": "~8.2.2",
    "eslint": "~8.57.0",
    "eslint-plugin-react-hooks": "~4.6.0",
    "eslint-plugin-react-refresh": "~0.4.6",
    "postcss": "~8.4.38",
    "tailwindcss": "~3.4.3",
    "typescript": "~5.4.5",
    "vite": "~5.2.10"
  }

共享库依赖项

  "dependencies": {
    "@syncfusion/ej2-popups": "~27.2.2",
    "@syncfusion/ej2-react-buttons": "~27.2.2",
    "@syncfusion/ej2-react-dropdowns": "~27.2.2",
    "@syncfusion/ej2-react-notifications": "27.2.2",
    "@syncfusion/ej2-react-popups": "~27.2.2",
    "dompurify": "^3.2.1",
    "dotenv": "^16.4.5",
    "react-router-dom": "^7.0.1",
    "rehype-stringify": "^10.0.1",
    "remark": "^15.0.1",
    "remark-parse": "^11.0.0",
    "remark-rehype": "^11.1.1",
    "unified": "^11.0.5",
    "uuid": "~9.0.1"
  },
  "devDependencies": {
    "@types/node": "^22.9.0",
    "@types/react": "^18.3.12",
    "@types/react-dom": "^18.3.1",
    "@types/react-router-dom": "^5.3.3",
    "@types/uuid": "~9.0.8",
    "@vitejs/plugin-react": "^4.3.3",
    "autoprefixer": "^10.4.19",
    "chokidar-cli": "^3.0.0",
    "cpx": "^1.5.0",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.4.38",
    "rimraf": "^6.0.1",
    "tailwindcss": "^3.4.14",
    "typescript": "^5.6.3",
    "vite": "^5.4.11"
  },
  "peerDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "~7.0.1",
    "tailwindcss": "^3.4.3"
  }
reactjs react-router-dom outlet
1个回答
0
投票

如果加载状态未按预期解决或用户未经过正确身份验证,则可能会出现您面临的问题(黑屏)。

所以尝试更新您的代码,如下所示:

import { useEffect, useState } from "react";
import { useAuth } from "./useAuth";
import { userServer } from "../api/userServer";
import { Outlet, Navigate } from "react-router-dom";

export const ProtectedRoute = () => {
  const { user, fetchUserClaims } = useAuth();
  const userAPI = userServer();
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkAuthentication = async () => {
      try {
        const isLoggedIn = await userAPI.getUserLoggedInStatus();
        if (!isLoggedIn) {
          setLoading(false);  // Stop loading and redirect
        } else if (!user) {
          await fetchUserClaims();
        } else {
          setLoading(false);  // User data is loaded, stop loading
        }
      } catch (error) {
        console.error("Error during authentication check:", error);
        setLoading(false);  // Stop loading even on error
      }
    };

    checkAuthentication();
  }, [user, fetchUserClaims, userAPI]);

  if (loading) {
    return <div>Loading Protected Content...</div>;  // Loading state
  }

  if (!user || !user.isLoggedIn) {
    return <Navigate to="/" />;  // Redirect to home if not authenticated
  }

  return <Outlet />;  // Render protected content if authenticated
};

export default ProtectedRoute;

解释:我们现在不再使用 window.location.href,而是使用从 React-router-dom 导航来在未经身份验证或出错时重定向用户。这确保了干净的重定向,无需重新加载页面,并与 React Router 无缝协作。

  • 只有在用户身份验证检查完成后,加载状态才会设置为 false。这可以防止在准备好之前渲染 Outlet。

  • 如果身份验证检查期间出现错误,我们将确保更新加载状态,并相应地停止进一步渲染或重定向。

  • 如果用户未通过身份验证或 user.isLoggedIn 为 false,则用户将被重定向到主页。否则,它会呈现 Outlet(受保护的路线)。

© www.soinside.com 2019 - 2024. All rights reserved.