如何防止当前页面在刷新时重定向到另一个页面?

How to prevent the current page from redirecting to another page when refreshing?

每次我重新加载个人资料页面时,它都会自行重定向到主页。我该如何解决这个问题?

用户成功登录后,他或她将被定向到主页。在主页上,有一个配置文件页面。我可以成功加载个人资料页面,但是,一旦我重新加载它,用户将再次被重定向到主页。

    //custom hook
export function useAuth() {
  const [currentUser, setCurrentUser] = useState();

  useEffect(() => {
    const unsub = onAuthStateChanged(auth, (user) => setCurrentUser(user));
    return unsub;
  }, []);

  return currentUser;
}

App.js

function App() {
  const currentUser = useAuth();
  const user = auth.currentUser;
  const navigate = useNavigate();


  useEffect(() => {
    const unsub = onAuthStateChanged(auth, (user) => {
      if (user) {
        // User is signed in, see docs for a list of available properties
        // https://firebase.google.com/docs/reference/js/firebase.User
        const uid = user.uid;
        console.log(uid);
        navigate("/Homepage");
        // ...
      } else {
        // User is signed out
        // ...
        navigate("/");
      }
    });

    return unsub;
  }, []);

  return (
    <div>
      <div>
        <Routes>
        
          {currentUser ? (
            <>
              <Route
                path="/"
                element={
                  <MainLayout>
                    <LoginPage />
                  </MainLayout>
                }
              />
              <Route path="/Homepage" element={<Home />} />
              <Route path="/Profile" element={<ProfilePage />} />
            </>
          ) : (
            <>
              <Route
                path="/"
                element={
                  <MainLayout>
                    <LoginPage />
                  </MainLayout>
                }
              />
            </>
          )}

          
        </Routes>
      </div>
    </div>
  );
}

export default App;

如果我 console.log(currentUser) 这就是它显示的内容:

也在:https://www.reddit.com/r/reactjs/comments/smfsro/how_to_prevent_the_page_from_redirecting_to/

受保护的路线:

{currentUser ? (
            <>
              <Route
                path="/"
                element={
                  <MainLayout>
                    <LoginPage />
                  </MainLayout>
                }
              />
              <Route path="/Homepage" element={<Home />} />
              <Route
                path="/Profile"
                element={
                  <PrivateRoute>
                    <ProfilePage />
                  </PrivateRoute>
                }
              />

             
            </>
          ) : (
            <>
              <Route
                path="/"
                element={
                  <MainLayout>
                    <LoginPage />
                  </MainLayout>
                }
              />
            </>
          )}

私有路由

import React from "react";
import { Navigate, Outlet, useLocation } from "react-router-dom";

import { useAuth } from "../../Firebase/utils";

const PrivateRoute = () => {
  const currentUser = useAuth();

  // //   const !currentUser = null; // determine if authorized, from context or however you're doing it

  // // If authorized, return an outlet that will render child elements
  // // If not, return element that will navigate to login page

  // return currentUser ? <Outlet /> : <Navigate to="/" />;

  let location = useLocation();

  if (!currentUser) {
    console.log(currentUser);
    return <Navigate to="/" state={{ from: location }} replace />;
  }
};

export default PrivateRoute;

我认为问题是 currentUser 没有默认值,所以当您第一次加载页面时它总是 undefined。您应该添加加载状态以确保在使用 currentUser 执行任何逻辑之前检查用户的状态。

注意:我没有运行下面的任何代码,所以可能会有一些错误,但这只是一个大概的参考。

export function useAuth() {
  const [isLoading, setIsLoading] = useState(true); // checking the user's status
  const [currentUser, setCurrentUser] = useState();

  useEffect(() => {
    const unsub = onAuthStateChanged(auth, (user) => {
        setCurrentUser(user)
        setIsLoading(false) // finished checking
    });
    return unsub;
  }, []);

  return {currentUser, isLoading};
}

因此,当您检查用户是否已登录时:

if(!isLoading && currentUser){
  // is finished loading and user is logged in
}

我还建议您将 currentUser 存储在上下文中而不是使用自定义挂钩,因为自定义挂钩不允许您在组件之间共享状态,并且每次使用它时都会挂载它。

相反,您的挂钩可用于从上下文中获取值。

像这样:

// store currentUser inside context
const AuthContext = createContext();

export const AuthProvider = ({children}) => {
  const [isLoading, setIsLoading] = useState(true);
  const [currentUser, setCurrentUser] = useState();

  useEffect(() => {
    const unsub = onAuthStateChanged(auth, (user) => {
        setCurrentUser(user)
        setIsLoading(false)
    });
    return unsub;
  }, []);

  return (
    <AuthContext.Provider value={{currentUser, isLoading}}>
      {children} 
    </AuthContext.Provider>
  )
}

// read context value with custom hook
export useAuth = () => useContext(AuthContext)

当您使用 useAuth 时它仍然是相同的,只是它从上下文中获取状态。

const {currentUser, isLoading} = useAuth()

---编辑问题---

上下文用于在整个应用程序中共享数据,当您需要在多个地方使用相同的数据时它非常有用。您可以存储数据并通过 提供者 向下传递,并通过 消费者 访问它。我不会详细介绍,但您可以在此处阅读有关 Context API 的更多信息:https://reactjs.org/docs/context.html#gatsby-focus-wrapper

关于AuthProvider的使用方法,一般可以这样包装在App.js

// App.js
<AuthProvider>
  <div>
    <Routes>{/* ... */}</Routes>
  </div>
</AuthProvider>

但在这种情况下,由于您已经在 App.js 中使用 currentUser,因此您必须将 AuthProvider 更新为 HOC,并将其包装在您的组件周围

// AuthContext
export const withAuthProvider = WrappedComponent => ({children}) => {
  // ... the rest is the same
}
// App.js
export default withAuthProvider(App);

这是因为上下文值只能在提供者所环绕的范围内访问。如果没有 HOC,您的提供者将包裹在您的路由中,这意味着该值无法在 App.js 本身中访问,但可以在 MainLayout、LoginPage 等中访问。然而,使用 HOC,因为您已经包裹了整个 App.js,它可以访问AuthContext中的值。