Jer*_*me 8 typescript react-router-dom dataloader
使用 React-Router V6,我尝试强类型化正在使用的数据加载器函数params以及useLoaderData钩子。
到目前为止,我必须做以下难看的事情:
A- 对于useLoaderData,需要强制 returnType :
const profil = useLoaderData() as TProfil;
Run Code Online (Sandbox Code Playgroud)
export declare function useLoaderData<T>(): T;我想创建一个通用的钩子而不是导出会更干净declare function useLoaderData(): unknown;
B-对于数据加载器,接收到的参数的类型是什么?不得不强迫any,但这太丑了。如何强类型化并在某处声明 params 由来自路由定义中的参数名称的“id”组成?
const careerDetailDataLoader = async ({ params }: any): Promise<TProfil> => {
const { id } = params;
const res = await fetch(`http://localhost:4000/careers/${id}`);
const data: TProfil = await res.json();
return data;
};
<Route path=":id" element={<CareerDetailsPage />} loader={careerDetailDataLoader} />
Run Code Online (Sandbox Code Playgroud)
小智 15
昨晚我第一次使用延迟加载器时遇到了和你一样的问题。
我想出了以下辅助函数作为使用时缺乏类型安全的解决方法react-router-dom:
// utils.ts
import { Await as RrdAwait, defer, LoaderFunctionArgs, useLoaderData as useRrdLoaderData } from "react-router-dom";
export function useLoaderData<TLoader extends ReturnType<typeof deferredLoader>>() {
return useRrdLoaderData() as ReturnType<TLoader>["data"];
}
export function deferredLoader<TData extends Record<string, unknown>>(dataFunc: (args: LoaderFunctionArgs) => TData) {
return (args: LoaderFunctionArgs) =>
defer(dataFunc(args)) as Omit<ReturnType<typeof defer>, "data"> & { data: TData };
}
export interface AwaitResolveRenderFunction<T> {
(data: Awaited<T>): React.ReactElement;
}
export interface AwaitProps<T> {
children: React.ReactNode | AwaitResolveRenderFunction<T>;
errorElement?: React.ReactNode;
resolve: Promise<T>;
}
export function Await<T>(props: AwaitProps<T>): JSX.Element {
return RrdAwait(props);
}
Run Code Online (Sandbox Code Playgroud)
用法:
// MainLayout.tsx
import { Await, deferredLoader, useLoaderData } from "./utils";
export const mainLayoutLoader = deferredLoader(args => ({
metrics: api.metrics.get()
}));
export const MainLayout: FC = () => {
const data = useLoaderData<typeof mainLayoutLoader>();
return (
<Suspense fallback={<SiderMenu />}>
<Await resolve={data.metrics}>
{metrics => <SiderMenu metrics={metrics} />}
</Await>
</Suspense>
);
};
Run Code Online (Sandbox Code Playgroud)
const router = createBrowserRouter([
{
path: "/",
element: <MainLayout />,
loader: mainLayoutLoader
}]);
Run Code Online (Sandbox Code Playgroud)
在哪里:
api.metrics.get()返回一个类型Promise<Metric[]>useLoaderData<typeof mainLayoutLoader>()返回一个类型{ metrics: Promise<Metric[]>; }metrics{metrics => ...}节点内部将<Await>被键入为Metric[]argsLoaderFunctionArgs是中定义的类型,其结果是forreact-router-dom的类型{ [key: string]: string | undefined; }args.params我想创建一个像导出声明函数 useLoaderData(): T; 这样的通用钩子会更干净。而不是导出声明函数 useLoaderData():unknown;
任何使用泛型的解决方案都只会隐藏必要的类型转换,as因此泛型版本会给人一种类型安全的错误感觉。
Remix完全具有您建议的通用性useLoaderData<T>()。在remix github repo中,详细解释了为什么他们弃用该函数的通用版本以及为什么useLoaderData()他们移植到 React-router v6 的函数不使用泛型。(另一个答案中建议的辅助函数与 remix 中已弃用的函数具有相同的问题。)
对于数据加载器,收到的参数类型是什么?
在 中react-router,有ActionFunctionArgs导致Params被定义(在 中@remix-run/router/dist/utils.d.ts/Params)如下:
export type Params<Key extends string = string> = {
readonly [key in Key]: string | undefined;
};
Run Code Online (Sandbox Code Playgroud)
但是,您可能想查看/sf/answers/5360570911/ ,了解有关 v6的参数类型loader和函数的类似问题(可能还有更清晰的解决方案) 。actionreact-router
| 归档时间: |
|
| 查看次数: |
8127 次 |
| 最近记录: |