我正在为 React“高阶组件”开发 TypeScript 函数。需要:
useQuery 类型函数useQuery 类型函数resultKey ,它确定查询结果是否应传播到组件中或嵌套在给定键下。这是我迄今为止的实现:
import React, { ComponentProps, FC } from "react";
import { UseQueryResult } from "react-query";
import { useParams } from "react-router-dom";
import { ReactQueryLoader } from "Components/Shared/Elements/ReactQueryLoader";
import { useErrorToast } from "Utils/toasts";
import { useQueryParams } from "Utils/uris";
/** The useQuery function returning the query result */
type QueryFunc = (...args: unknown[]) => UseQueryResult;
/** Function returning array of args to pass to the query. Func is fed an object with URL params and passed component props. */
type GetArgsFunc<Props> = (getArgsArgs: {
params: Record<string, string>;
props: Props;
queryParams: Record<string, unknown>;
}) => unknown[];
/** The string value to pass the result under to the child component. If undefined, result is spread */
type ResultKey = string | undefined;
type QueryTriplet<Props = Record<string, unknown>> = [QueryFunc, GetArgsFunc<Props>, ResultKey];
type QueryResult = Record<string, unknown> | Record<string, Record<string, unknown>>;
/**
* Sort of the React Query version of React Redux's `connect`. This provides a neater interface for "wrapping" a component
* with the API data it requires. Until that data resolves, a loading spinner is shown. If an error hits, a toast is shown.
* Once it resolves, the data is passed to the underlying component.
*
* This "wrapper" is a bit more complex than the typical useQuery pattern, and is mostly better for cases where you want the "main" component
* to receive the data unconditionally, so it can use it in a useEffect, etc.
*
* @param Component The Component to be rendered once the provided query has been resolved
* @param useQuery The React Query hook to be resolved and passed to the Component
* @param getArgs A function returning an ordered array of args to pass to the query func.
* getArgs takes an object with URL `params` and passed `props`
* @param resultKey The name of the prop to pass the query data to the Component as.
* If not provided, the incoming data from the query will be spread into the Component's props.
*
* @example
*
* const OrgNameContent = ({ org }: { org: CompleteOrg }) => {
* const { name } = org;
* return <div>Org name: {name}</div>
* }
*
* export const OrgName = withQuery(
* OrgNameContent,
* useGetOrg,
* ({ params }) => [params.uuid], // useGetOrg takes a single uuid param. The uuid comes from the URL.
* "org" // The OrgNameContent component expects an "org" prop, so we pass the data as that prop.
* );
*/
export function withQuery<QueryFetchedKeys extends string = "", Props = Record<string, unknown>>(
Component: FC<Props>,
useQuery: QueryFunc,
getArgs: GetArgsFunc<Props>,
resultKey: ResultKey = undefined
) {
type NeededProps = Omit<Props, QueryFetchedKeys>;
const ComponentWithQuery: FC = (props: NeededProps) => {
const showErrorToast = useErrorToast();
const params = useParams();
const queryParams = useQueryParams();
const queryArgs = getArgs({ params, props, queryParams });
const query = useQuery(...queryArgs) as UseQueryResult<QueryResult>;
return (
<ReactQueryLoader useQueryResult={query} handleError={showErrorToast}>
{({ data }) => {
const resultProps = (resultKey ? { [resultKey]: data } : data) as
| QueryResult
| Record<string, QueryResult> as Props;
return <Component {...props} {...resultProps} />;
}}
</ReactQueryLoader>
);
};
return ComponentWithQuery as FC<NeededProps>;
}
它工作得很好,但我在获取正确的类型时遇到了困难。理想情况下,我会传入一个组件(已键入),并且该函数将从该组件“推断”该组件需要的最终一组道具是什么。然后,在该组件上调用 withQuery 的结果将返回一个具有单独的、较小的所需道具集的组件,因为 withQuery 调用提供不需要由父组件传入的道具。 p>
例如,如果我这样做:
type SomeComponentProps = { uuid: string, org: Org };
const SomeComponentBase: FC<SomeComponentProps> = ({ org }) => (
<span>{org.name}</span>
)
// Would expect `uuid` as a prop, but not `org`
export const SomeComponent = withQuery(
SomeComponent,
useGetOrg, // This query expects a uuid arg, and returns an org
({ props }) => [props.uuid], // Grab the passed uuid, and pass it in as the first and only arg to the useOrg function
'org' // Assert that the result of the query (an org), should be passed as a prop under the key "org"
)
withQuery 函数理想情况下应该足够“智能”:
resultKey,所以该 prop 是从查询传入的,不需要从外部传入。因此,可以从导出的组件类型中省略 Omitted。超级,超级理想,如果输入 useGetOrg ,并且没有传递 resultKey (意味着查询的结果作为 props 传播), withQuery 函数将能够检测到该响应的所有键由查询提供,因此不需要由渲染父组件传入。
这可能吗?目前这有点超出了我的 TypeScript 能力。
你能帮我重写这个方法来处理这种类型推断,这样父组件只需要传入 withQuery 本身不提供的 props 吗?
或者,如果这是不可能的,也许当你调用 withQuery 时,你可以传入生成组件的 props 类型?
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
如果我从您的问题中理解正确,您想要推断传递到
withQuery的组件类型,并从其 props 中删除传递到resultKey参数的属性。您可以使用
React.ComponentProps实用程序类型来提取组件的 props 类型。然后,您可以使用Omit类型实用程序从组件的 props 中提取传递到resultKey参数的属性。请参阅此答案,了解有关从组件本身提取 React 组件 Prop 类型的更多信息。
或者,如果您想推断 Query 的结果类型并根据该结果类型从 props 中删除属性,您可以使用
ResultType实用程序类型和keyof来实现功能: