Skip to content

数据模式

  • createBrowserRouter()
  • RouterProvider
  • loader
  • useLoaderData()
  • action
  • useActionData()
  • Form

配置路由

这是个简单的示例:

router.ts

ts
import { createBrowserRouter } from "react-router";
import Home from './pages/...'
...

const router = createBrowserRouter([
    {
        path: "/",
        Component: Layout,
        children: [
            { index: true, Component: Home },
            { path: "about", Component: About },
            {
                path: "auth",
                Component: AuthLayout,
                children: [
                    { path: "login", Component: Login },
                    { path: "register", Component: Register },
                ],
            },
        ],
    },
]);

export default router

App.tsx

ts
import { RouterProvider } from 'react-router'
import router from './router'

function App() {

    return (
        <>
            <RouterProvider router={router}/>
        </>
    )
}

路由对象

在路由配置中还可以做数据加载操作

ts
import { createBrowserRouter, useLoaderData } from "react-router";

createBrowserRouter([
    {
        path: "/teams/:teamId",
        loader: async ({ params }) => {
            let team = await fetchTeam(params.teamId);
            return { name: team.name };
        },
        Component: Team,
    },
]);

function Team() {
    let data = useLoaderData();
    return <h1>{data.name}</h1>;
}
  • loader: 该属性可以返回一个Promise,当Promise resolve时,组件会重新渲染,并且将resolve的值作为useLoaderData的返回值。
  • useLoaderData: 用于接收返回值

嵌套路由

路由可以通过 children 嵌套在父路由内。

ts
createBrowserRouter([
    {
        path: '/dashboard',
        Component: Dashboard,
        children: [
            { index: true, Component: Home },
            { path: 'settings', Component: Settings }
        ]
    }
])

子路由通过父路由中的 <Outlet /> 进行渲染

ts
import { Outlet } from 'react-router'

function Dashboard() {
    return (
        <>
            <h1>Dashboard</h1>
            <Outlet />
        </>
    )
}

middleware

这是一个在导航前后顺序执行的中间件,它提供了一个统一的地方来执行诸如 日志记录身份验证 之类的操作。

next 函数会继续向下执行链,也就是说,在这里可以调用 next() 来继续执行下一个中间件。

ts
createBrowserRouter([
    {
        path: '/',
        middleware: [loggingMiddleware],
        loader: rootLoader,
        Commponent: Root,
        children: [{
            path: 'auth',
            middleware: [authMiddleware],
            loader: authLoader,
            Component: Auth,
            children: [...]
        }]
    }
])

async function loggingMiddleware({ request }, next) {
    // 前置操作
    let url = new URL(request.url);
    console.log(`Starting navigation: ${url.pathname}${url.search}`);
    const start = performance.now();

    // 进入下一个中间件
    await next();

    // 后置操作
    const duration = performance.now() - start;
    console.log(`Navigation completed in ${duration}ms`);
}

const userContext = unstable_createContext<User>();

async function authMiddleware ({ context }) {
    const userId = getUserId();

    if (!userId) {
        throw redirect("/login");
    }

    context.set(userContext, await getUserById(userId));
};

当访问 /auth 是执行顺序是

loggingMiddleware

    next()

authMiddleware

    next()

rootLoader + authLoader

渲染 Root + Auth

执行 authMiddleware next() 后的代码

执行 loggingMiddleware next() 后的代码

lazy(懒加载)

大多数属性都可以懒加载导入,以减少初始包的大小。

ts
createBrowserRouter([
    {
        path: "/app",
        lazy: async () => {
            const {default: Component} = await import('./pages/App/AppPage')
            return { Component }
        },
    },
]);

除了Component,其他属性也可以通过这种方式

ts
createBrowserRouter([
    {
        path: "/app",
        lazy: async () => {
            const [{ default: Component }, { default: loader }] = await Promise.all([
                import("./pages/App/AppPage"),
                import("./app-loader"),
            ]);
            return { Component, loader };
        },
    },
]);

如果组件或方法是通过 export 而不是 export default 导出那么写法可以更简洁

注意:这种方式组件名必须为 Component

./pages/Login:

ts
export function Component() {
    return (
        <div>
            <h1>Login</h1>
            <input />
            <button>登录</button>
        </div>
    );
}

router:

ts
{
    path: "/login",
    lazy: () => import("./pages/Login"),
}