ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

React Router 如何用 dataStrategy 自定义 loader 与 action 的执行顺序和并发策略?

React Router 如何用 dataStrategy 自定义 loader 与 action 的执行顺序和并发策略? React Router 如何用 dataStrategy 自定义 loader 与 action 的执行顺序和并发策略【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router当你需要改变 React Router 默认的所有 loader 并行执行这一行为——比如让多个 handler 按顺序跑、先执行 middleware 再并行跑 loader、或者干脆把多个 loader 的数据合并成一次请求——就需要在创建 data router 时传入dataStrategy选项。这个选项让你完全控制action/loader函数的执行方式。需要注意按 Data Strategy 文档的说法这是一个面向高级场景的低层 API它会覆盖 React Router 对action/loader的内部执行逻辑写错会破坏应用代码使用前要谨慎并做相应测试。适用前提data router 的opts.dataStrategydataStrategy作为第二个参数opts的一项传入 data router 的创建函数createBrowserRouter 文档对opts.dataStrategy的定义是Override the default data strategy of running loaders in parallel并指向 how-to 文档。仓库中 createHashRouter 与 createMemoryRouter 的文档同样列出了该选项因此这条路径适用于 data 模式下用createBrowserRouter/createHashRouter/createMemoryRouter创建路由器的场景。一个自定义dataStrategy函数除了loader/action会收到的request、params、context之外还会收到三个用于控制执行的字段见 docs/how-to/data-strategy.mdmatches当前request匹配到的路由的DataStrategyMatch数组runClientMiddleware执行匹配路由 middleware 的辅助函数fetcherKey如果是 fetcher 请求而非导航则为其 fetcher key导航执行为null每个DataStrategyMatch是普通路由匹配加上几个额外字段shouldCallHandler判断本次请求是否应该调用该路由 handler 的函数加载导航时对新路由和需要 revalidate 的已有路由返回 true提交导航只对被提交路由的 action 返回 truefetcher 调用只对 fetcher 路由返回 trueshouldRevalidateArgs要传给该路由shouldRevalidate的参数shouldLoad布尔字段已废弃用shouldCallHandler替代resolve调用路由 handler 的函数也可以传入回调自定义 handler 的执行方式函数最终要返回Recordstring, DataStrategyResult键为执行过 handler 的route.id。DataStrategyResult是表明 handler 返回还是抛错的包装对象类型定义interface DataStrategyResult { type: data | error; result: unknown; // data, Error, Response, data() }最短路径保留并行执行只加执行观测先跑通透传默认行为的最小实现。文档给出的基础示例在 handler 执行前后加日志——这也是验证你的策略是否生效、以什么顺序生效最直接的方式console.log输出属于文档示例let router createBrowserRouter(routes, { async dataStrategy({ matches, request, runClientMiddleware, }) { // Determine which matches are expected to be executed for this request. // - For loading navigations, this will return true for new routes existing // routes requiring revalidation // - For submission navigations, this will only return true for the action route // - For fetcher calls, this will only return true for the fetcher route const matchesToLoad matches.filter((m) m.shouldCallHandler(), ); // For each match that we want to execute, call match.resolve() to execute // the handler and store the result const results: Recordstring, DataStrategyResult {}; await runClientMiddleware(() Promise.all( matchesToLoad.map(async (match) { console.log(Processing ${match.route.id}); // The resolve function calls through to the route handler results[match.route.id] await match.resolve(); }), ), ); return results; }, });判断标准触发一次导航后控制台按匹配顺序打印Processing route.id页面各路由的 loader 数据正常渲染说明策略被正确调用且没有破坏默认行为。仓库中的>async function dataStrategy({ matches }) { let results []; for (let match of matches) { let result await match.resolve((handler) { return handler(ctx); // ctx 见下文向 handler 传第二个参数 }); results.push(result); } return results; }注意这段来自设计文档返回的是数组而当前 API 要求返回按route.id键控的Recordstring, DataStrategyResult所以实际落地时把收集结果的写法换成 how-to 文档示例中的results[match.route.id] ...形式并在循环里只对shouldCallHandler()为 true 的 match 调用resolve()。串行与并行只是调度方式的区别做什么、对哪些 match 做的判断规则不变。向 handler 传第二个参数resolve的回调如果你想在调用时给 handler 注入额外参数可以给match.resolve()传一个回调。文档示例假设 loader 形如function loader({ request }, customContext) {...}// In your dataStrategy, you can pass this context from inside a resolve callback await Promise.all( matchesToLoad.map((match, i) match.resolve((handler) { let customContext getCustomContext(); // Call the handler and pass a custom parameter as the handlers second argument return handler(customContext); }), ), );以上为文档中dataStrategy内部的代码片段matchesToLoad指前面shouldCallHandler过滤后的 match 集合传给handler的值会成为loader/action的第二个参数。与 middleware、revalidation 配合执行路由 middleware。如果你的路由上用了middleware需要用runClientMiddleware把 handler 执行包在 middleware 链的末尾即 middleware 先串行执行完再跑 handlerlet router createBrowserRouter(routes, { async dataStrategy({ matches, request, runClientMiddleware, }) { const matchesToLoad matches.filter((m) m.shouldCallHandler(), ); const results: Recordstring, DataStrategyResult {}; // Run middleware and execute handlers at the end of the middleware chain await runClientMiddleware(() Promise.all( matchesToLoad.map(async (match) { results[match.route.id] await match.resolve(); }), ), ); return results; }, });runClientMiddleware接收的参数与dataStrategy相同所以它也能把独立的dataStrategy实现组合进来const loggingDataStrategy: DataStrategyFunction () { /* ... */ }; let router createBrowserRouter(routes, { async dataStrategy({ runClientMiddleware }) { let results await runClientMiddleware( loggingDataStrategy, ); return results; }, });middleware 本身的用法见 docs/how-to/middleware.md。自定义 revalidation 行为。想把自定义的重验证判断传入路由级shouldRevalidate时给match.shouldCallHandler()传自己的defaultShouldRevalidate值路由级shouldRevalidate会收到的参数在match.shouldRevalidateArgs上const matchesToLoad matches.filter((match) { let defaultShouldRevalidate customShouldRevalidate( match.shouldRevalidateArgs, ); return match.shouldCallHandler(defaultShouldRevalidate); });可选进阶middleware 串行 loader 并行的完整组合Data Strategy 的 Custom Middleware 高级示例完整演示了先按顺序跑 middleware 往context上写数据再并行跑所有 loader 并把context作为第二个参数传入——这正是自定义执行顺序与并发策略的典型形态const routes [ { id: parent, path: /parent, loader({ request }, context) { // ... }, handle: { async middleware({ request }, context) { context.parent PARENT MIDDLEWARE; }, }, children: [ { id: child, path: child, loader({ request }, context) { // ... }, handle: { async middleware({ request }, context) { context.child CHILD MIDDLEWARE; }, }, }, ], }, ]; let router createBrowserRouter(routes, { async dataStrategy({ matches, params, request }) { // Run middleware sequentially and let them add data to context let context {}; for (const match of matches) { if (match.route.handle?.middleware) { await match.route.handle.middleware( { request, params }, context, ); } } // Run loaders in parallel with the context value let matchesToLoad matches.filter((m) m.shouldCallHandler(), ); let results await Promise.all( matchesToLoad.map((match, i) match.resolve((handler) { // Whatever you pass to handler will be passed as the 2nd parameter // to your loader/action return handler(context); }), ), ); return results.reduce( (acc, result, i) Object.assign(acc, { [matchesToLoad[i].route.id]: result, }), {}, ); }, });文档同时指出既然 React Router 已内置 middleware用dataStrategy实现自研 middleware 已是不太可能出现的用例只在确实需要自定义 middleware 时才走这条路。另一个可选分支是自定义 handler把route.loader设为true使其算作有 loader、把数据片段放在route.handle上在dataStrategy里直接发一次聚合请求如单个 GraphQL 请求然后按routeId拆回各路由的结果——此时完全不调用match.resolve()因为不想执行路由上定义的 handler。该示例全文见 docs/how-to/data-strategy.md。限制与排查官方定位是低层 APIThis overrides React Routers internal handling ofaction/loaderexecution, and if done incorrectly will break your app code。改动后用文档建议的日志方式console.log各route.id核对执行顺序与数量再验证页面数据渲染。已从shouldLoad迁移到shouldCallHandler的策略必须预过滤后再resolve()否则会对本不该执行的 match 也触发 handler。想确认参数与结果是否符合预期可参考仓库单测 contenteditable="false">【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表