React Router v6.10 AuthContext + createBrowserRouter - 导航问题

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

我正在尝试使用反应路由器创建一个具有受保护路由的应用程序。我使用官方示例(https://github.com/remix-run/react-router/tree/dev/examples/auth)来创建应用程序。

该示例运行良好。它使用

<BrowserRouter>
<Route>
组件等...但是,文档提到了
createBrowserRouter
: “这是所有 React Router Web 项目的推荐路由器。它使用 DOM History API 来更新 URL 并管理历史堆栈。”

所以我尝试将

createBrowserRouter
实现到第一个示例中,但出现了问题。当用户尝试登录时,
auth.signin()
存储该值,但在第二次尝试后重定向到
/protected
有效。有谁可以解释一下,为什么
navigate()
组件中的
LoginPage
第一次不起作用?或者如何解决这个问题?

这是使用

createBrowserRouter

更新的代码

main.tsx

import './index.css';

import React from 'react';

import ReactDOM from 'react-dom/client';

import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
);

应用程序.tsx

import * as React from 'react';

import {
    createBrowserRouter,
    Link,
    Navigate,
    Outlet,
    RouterProvider,
    useLocation,
    useNavigate,
} from 'react-router-dom';

import { fakeAuthProvider } from './auth';

const router = createBrowserRouter([
    {
        element: <Layout />,
        children: [
            {
                path: '/',
                element: <PublicPage />,
            },
            {
                path: '/login',
                element: <LoginPage />,
            },
            {
                path: '/protected',
                element: (
                    <RequireAuth>
                        <ProtectedPage />
                    </RequireAuth>
                ),
            },
        ],
    },
]);

export default function App() {
    return (
        <AuthProvider>
            <h1>Auth Example</h1>

            <p>
                This example demonstrates a simple login flow with three pages: a public page, a protected page, and a
                login page. In order to see the protected page, you must first login. Pretty standard stuff.
            </p>

            <p>
                First, visit the public page. Then, visit the protected page. You're not yet logged in, so you are
                redirected to the login page. After you login, you are redirected back to the protected page.
            </p>

            <p>
                Notice the URL change each time. If you click the back button at this point, would you expect to go back
                to the login page? No! You're already logged in. Try it out, and you'll see you go back to the page you
                visited just *before* logging in, the public page.
            </p>

            <RouterProvider router={router} />
        </AuthProvider>
    );
}

function Layout() {
    return (
        <div>
            <AuthStatus />

            <ul>
                <li>
                    <Link to="/">Public Page</Link>
                </li>
                <li>
                    <Link to="/protected">Protected Page</Link>
                </li>
            </ul>

            <Outlet />
        </div>
    );
}

interface AuthContextType {
    user: any;
    signin: (user: string, callback: VoidFunction) => void;
    signout: (callback: VoidFunction) => void;
}

let AuthContext = React.createContext<AuthContextType>(null!);

function AuthProvider({ children }: { children: React.ReactNode }) {
    let [user, setUser] = React.useState<any>(null);

    let signin = (newUser: string, callback: VoidFunction) => {
        return fakeAuthProvider.signin(() => {
            setUser(newUser);
            callback();
        });
    };

    let signout = (callback: VoidFunction) => {
        return fakeAuthProvider.signout(() => {
            setUser(null);
            callback();
        });
    };

    let value = { user, signin, signout };

    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

function useAuth() {
    return React.useContext(AuthContext);
}

function AuthStatus() {
    let auth = useAuth();
    let navigate = useNavigate();

    if (!auth.user) {
        return <p>You are not logged in.</p>;
    }

    return (
        <p>
            Welcome {auth.user}!{' '}
            <button
                onClick={() => {
                    auth.signout(() => navigate('/'));
                }}
            >
                Sign out
            </button>
        </p>
    );
}

function RequireAuth({ children }: { children: JSX.Element }) {
    let auth = useAuth();
    let location = useLocation();

    if (!auth.user) {
        // Redirect them to the /login page, but save the current location they were
        // trying to go to when they were redirected. This allows us to send them
        // along to that page after they login, which is a nicer user experience
        // than dropping them off on the home page.
        return <Navigate to="/login" state={{ from: location }} replace />;
    }

    return children;
}

function LoginPage() {
    let navigate = useNavigate();
    let location = useLocation();
    let auth = useAuth();

    let from = location.state?.from?.pathname || '/';

    function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
        event.preventDefault();

        let formData = new FormData(event.currentTarget);
        let username = formData.get('username') as string;

        auth.signin(username, () => {
            // Send them back to the page they tried to visit when they were
            // redirected to the login page. Use { replace: true } so we don't create
            // another entry in the history stack for the login page.  This means that
            // when they get to the protected page and click the back button, they
            // won't end up back on the login page, which is also really nice for the
            // user experience.
            navigate(from, { replace: true });
        });
    }

    return (
        <div>
            <p>You must log in to view the page at {from}</p>

            <form onSubmit={handleSubmit}>
                <label>
                    Username: <input name="username" type="text" />
                </label>{' '}
                <button type="submit">Login</button>
            </form>
        </div>
    );
}

function PublicPage() {
    return <h3>Public</h3>;
}

function ProtectedPage() {
    return <h3>Protected</h3>;
}

问题和我尝试过的内容已在前面的文本区域中描述过

reactjs authentication react-router navigation react-router-dom
1个回答
0
投票

这里有更新的代码,涵盖了您所询问的如何保护 createBrowserRouter 创建的路由而不是使用传统的 JSX 组件:

https://github.com/remix-run/react-router/tree/main/examples/auth-router-provider

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.