我正在為 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 是從查詢傳入的,不需要從外部傳入。因此,可以從匯出的元件類型中省略 Omit
ted。 超級,超級理想,如果輸入useGetOrg
,並且沒有傳遞resultKey (意味著查詢的結果作為props 傳播), withQuery
函數將能夠偵測到該回應的所有鍵由查詢提供,因此不需要由渲染父元件傳入。
這可能嗎?目前這有點超出了我的 TypeScript 能力。
你能幫我重寫這個方法來處理這種類型推斷,這樣父元件只需要傳入 withQuery
本身不提供的 props 嗎?
或者,如果這是不可能的,也許當你呼叫 withQuery
時,你可以傳入生成元件的 props 類型?
P粉2036487422024-01-17 10:53:01
如果我從您的問題中理解正確,您想要推斷傳遞到 withQuery
的元件類型,並從其 props 中刪除傳遞到 resultKey
參數的屬性。 < /p>
您可以使用 React.ComponentProps
實用程式類型來提取元件的 props 類型。然後,您可以使用 Omit
類型公用程式從元件的 props 中提取傳遞到 resultKey
參數的屬性。
type ComponentProps = React.ComponentPropstype NeededProps = Omit
請參閱此答案,以了解有關從元件本身提取 React 元件 Prop 類型的更多資訊。
或者,如果您想推斷Query 的結果類型並根據該結果類型從props 中刪除屬性,您可以使用ResultType
實用程式類型和keyof
來實現功能:
type KeysOfDataReturnType = keyof ReturnType['data']; type NeededProps = Omit ;