反应私有路由和异步 api 调用

React private route and asynchronous api call

我需要保护我的 React 应用程序的一些路由,为此我创建了一个私有路由组件,如下所示:

import React from 'react';
import {useAuth} from "../../context/AuthContextProvider";
import {Navigate} from "react-router-dom";
import {useLocation} from "react-router";

const PrivateRoute = ({children}) => {
    const {user, isAuthenticating, isAuthenticated} = useAuth();
    const location = useLocation();
    console.log(user, isAuthenticating, isAuthenticated);
    return isAuthenticated ? children : <Navigate to="/sign-in" state={{from: location}} replace/>;
}

export default PrivateRoute;

在身份验证上下文提供程序中,我检查用户是否已登录,如下所示:

import React, {createContext, useContext, useEffect, useState} from "react";
import {useMeMutation} from "../data/user";

const AuthContext = createContext();
export const useAuth = () => useContext(AuthContext);

const AuthContextProvider = ({children}) => {

    const {mutate: me} = useMeMutation();

    const [auth, setAuth] = useState({
        user: null,
        isAuthenticating: null,
        isAuthenticated: null
    });


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

    const revalidate = () => {
        return me({}, {
            onSuccess: ({data}) => {
                console.log(data);
                setAuth({
                    user: data,
                    isAuthenticating: false,
                    isAuthenticated: true
                });
            },
            onError: (error) => {
                if ((error.response && error.response.status === 401) ||
                    (error.response && error.response.status === 403)) {
                    setAuth({
                        user: null,
                        isAuthenticating: false,
                        isAuthenticated: false
                    });
                }
            },
        });
    };

    const checkAuthentication = () => {
        if (auth.isAuthenticated === null) {
            revalidate();
        }
    };

    return (
        <AuthContext.Provider value={{
            ...auth,
            revalidate
        }}>{children}</AuthContext.Provider>
    );
};

export default AuthContextProvider;

此代码的问题是在 api 端检查用户之前显示登录组件。 我的路线是这样的:

<Routes>
    <Route element={<PublicLayout/>}>
        {publicRoutes.map(({component: Component, path, exact}) => (
            <Route
                path={`/${path}`}
                key={path}
                exact={exact}
                element={<Component/>}
            />
        ))}
    </Route>

    <Route element={<PrivateRoute><PrivateLayout/></PrivateRoute>}>
        {privateRoutes.map(({component: Component, path, exact}) => (
            <Route
                path={`/${path}`}
                key={path}
                exact={exact}
                element={<Component/>}
            />
        ))}
    </Route>
</Routes>

在私有路由组件中,我需要停止对这两个状态的渲染:

  • 当用户第一次来到登录页面时,isAuthenticated 的值应该为null,并且当该值为false 时渲染私有路由,

  • 当用户点击登录按钮时,我需要将 isAuthenticating 设置为 true,在这种情况下用户的状态尚不清楚,所以我还需要阻止 private 的呈现路线

         const PrivateRoute = ({children}) => {
             const {user, isAuthenticating, isAuthenticated} = useAuth();
             const location = useLocation();
             if (isAuthenticated === null || isAuthenticating === true) {
                 return null;
             }
             return isAuthenticated ? children : <Navigate to="/sign-in" state={{from: location}} replace/>;
         }
    

并且在上下文提供程序中,我需要在调用 api 的开头指定 isAuthenticating,如下所示:

         const revalidate = () => {
                setAuth({
                    user: null,
                    isAuthenticating: true,
                    isAuthenticated: false
                });
                return me({}, {}
                ...