繁体   English   中英

React router 6+:如何在路由加载器中强类型化 params 选项?

[英]React router 6+ : how to strongly type the params option in route loader?

有路由器

export const router = createBrowserRouter([
  {
    path: '/todos/:todoId',
    element: <Todo />,
    loader: todoLoader,
  }
]);

有装载机

export const loader: LoaderFunction = async ({ params }) => {
  return await fetchData(params.todoId);
};

如何根据路径键入参数?

等待路径中指定参数的突出显示。

更新:感谢@DrewReese @LindaPaiste 来到以下解决方案。

const PathNames = {
  todoDetail: '/todos/:idTodo',
} as const;

interface Args extends ActionFunctionArgs {
  params: Params<ParamParseKey<typeof PathNames.todoDetail>>;
}

export const loader: LoaderFunction = async ({ params }:Args) => {
  return await fetchData(params.todoId);
};

初级方法

declare type _PathParam<Path extends string> = Path extends `${infer L}/${infer R}`? _PathParam<L> | _PathParam<R>: Path extends `:${infer Param}`? Param extends `${infer Optional}?`? Optional: Param: never; /** * Examples: * "/a/b/*" -> "*" * ":a" -> "a" * "/a/:b" -> "b" * "/a/blahblahblah:b" -> "b" * "/:a/:b" -> "a" | "b" * "/:a/b/:c/*" -> "a" | "c" | "*" */ declare type PathParam<Path extends string> = Path extends "*"? "*": Path extends `${infer Rest}/*`? "*" | _PathParam<Rest>: _PathParam<Path>; export declare type ParamParseKey<Segment extends string> = [ PathParam<Segment> ] extends [never]? string: PathParam<Segment>; /** * The parameters that were parsed from the URL path. */ export declare type Params<Key extends string = string> = { readonly [key in Key]: string | undefined; };

声明路径的映射/对象(键-值),您可以使用 RRD 的ParamsParamParseKey实用程序类型来提取路由路径参数(方法归功于 LindaPaiste 和 Qvazi )。

const Paths = {
  todoDetail: "/todos/:idTodo",
} as const;

interface TodoLoaderArgs extends ActionFunctionArgs {
  params: Params<ParamParseKey<typeof Paths.todoDetail>>;
}

const todoLoader: LoaderFunction = async ({ params }: TodoLoaderArgs) => {
  return await fetchData(params.idTodo);
};

在此处输入图像描述

const router = createBrowserRouter([
  {
    path: Paths.todoDetail,
    element: <Todo />,
    loader: todoLoader as LoaderFunction
  },
]);

但是, fetchData function 需要一个string类型,因此这里存在不兼容性,因为params.idTodo被键入为string | undefined string | undefined 它通过在访问之前进行条件检查来修复

const todoLoader: LoaderFunction = async ({ params }: TodoLoaderArgs) => {
  return params.idTodo ? await fetchData(params.idTodo) : null;
};

或断言它是非空的,例如fetchData(params.idTodo!)或者您可以提供后备,例如fetchData(params.idTodo?? "") 也许回退可以是数据获取的一些默认查询参数值。

const todoLoader: LoaderFunction = async ({ params }: TodoLoaderArgs) => {
  return await fetchData(params.idTodo ?? "");
};

编辑 react-router-6-how-to-strongly-type-the-params-option-in-route-loader (forked)

次要方法

这是一个有点迂回的方法,但这似乎是正确输入的,并且在运行的 codesandbox 中工作,虽然可能有点“hackish”(我的 Typescript foo 不是很好)。 要点是您需要覆盖加载程序 function args参数,以便您可以覆盖params属性以包含要在加载程序中访问的路径参数。

要覆盖的加载程序定义:

 /** * The parameters that were parsed from the URL path. */ export declare type Params<Key extends string = string> = { readonly [key in Key]: string | undefined; }; /** * @private * Arguments passed to route loader/action functions. Same for now but we keep * this as a private implementation detail in case they diverge in the future. */ interface DataFunctionArgs { request: Request; params: Params; context?: any; } /** * Arguments passed to loader functions */ export interface LoaderFunctionArgs extends DataFunctionArgs { } /** * Route loader function signature */ export interface LoaderFunction { (args: LoaderFunctionArgs): Promise<Response> | Response | Promise<any> | any; }

新的接口声明和用法:

import {
  RouterProvider,
  createBrowserRouter,
  Navigate,
  useLoaderData,
  LoaderFunction,
  LoaderFunctionArgs
} from "react-router-dom";

interface TodoLoaderFunctionArgs extends Omit<LoaderFunctionArgs, "params"> {
  params: {
    todoId: string;
  };
}

interface TodoLoaderFunction extends Omit<LoaderFunction, "args"> {
  (args: TodoLoaderFunctionArgs):
    | Promise<Response>
    | Response
    | Promise<any>
    | any;
}

const todoLoader: TodoLoaderFunction = async ({ params }) => {
  return await fetchData(params.todoId);
};

const router = createBrowserRouter([
  {
    path: "/todos/:todoId",
    element: <Todo />,
    loader: todoLoader as LoaderFunction
  },
]);

在此处输入图像描述

编辑 react-router-6-how-to-strongly-type-the-params-option-in-route-loader

次要方法 #2

另一种可能更简单的方法是简单地重铸params道具。

interface Params {
  todoId: string
}

const todoLoader: LoaderFunction = async ({ params }) => {
  const typedParams = params as unknown as Params;
  return await fetchData(typedParams.todoId);
};

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM