Dapatkan jenis Props daripada komponen dan tinggalkan sifat di dalamnya
P粉704066087
P粉704066087 2024-01-16 12:55:57
0
1
563

Saya sedang membangunkan fungsi TypeScript untuk "komponen peringkat tinggi" React. Diperlukan:

  • Satu komponen,
  • Pertanyaan Bertindak Balas useQuery Fungsi Jenis
  • Mengembalikan tatasusunan parameter dan menghantarnya ke fungsi taip di atas useQuery
  • Pilihan resultKey , yang menentukan sama ada hasil pertanyaan perlu disebarkan ke dalam komponen atau bersarang di bawah kunci yang diberikan.

Ini adalah pelaksanaan saya setakat ini:

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>;
}

Ia berfungsi dengan baik, tetapi saya menghadapi masalah mendapatkan jenis yang betul. Sebaik-baiknya, saya akan memasukkan komponen (ditaip) dan fungsi itu akan "menyimpulkan" daripada komponen itu set alat peraga akhir yang diperlukan oleh komponen itu. Kemudian, memanggil panggilan withQuery 的结果将返回一个具有单独的、较小的所需道具集的组件,因为 withQuery pada komponen itu menyediakan prop yang tidak perlu diteruskan oleh komponen induk.

Sebagai contoh, jika saya lakukan:

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 Fungsi sepatutnya cukup "pintar":

  1. Simpulkan jenis prop "penuh" (org dan uuid) daripada komponen yang diluluskan
  2. Fahami bahawa memandangkan "org" ialah resultKey,所以该 prop 是从查询传入的,不需要从外部传入。因此,可以从导出的组件类型中省略 Omit, prop dihantar masuk dari pertanyaan dan tidak perlu dihantar masuk dari luar. Oleh itu, Omitted boleh diabaikan daripada jenis komponen yang dieksport.

Super, sangat ideal, jika useGetOrg dimasukkan dan tiada resultKey diluluskan (bermaksud hasil pertanyaan disebarkan sebagai prop), useGetOrg ,并且没有传递 resultKey (意味着查询的结果作为 props 传播), withQuery fungsi akan dapat mengesan bahawa semua kunci respons disediakan oleh pertanyaan, jadi tidak perlu Dilalui oleh komponen induk rendering.

Adakah ini mungkin? Ini sedikit di luar keupayaan TypeScript saya pada masa ini.

Bolehkah anda membantu saya mengatasi kaedah ini untuk mengendalikan inferens jenis ini supaya komponen induk hanya perlu melepasi prop withQuery yang ia tidak menyediakan dirinya sendiri?

Atau, jika itu tidak mungkin, mungkin apabila anda memanggil withQuery anda boleh memasukkan jenis prop komponen yang dijana?

P粉704066087
P粉704066087

membalas semua(1)
P粉203648742

Jika saya faham dengan betul daripada soalan anda, anda ingin membuat kesimpulan sifat yang dihantar kepada parameter withQuery 的组件类型,并从其 props 中删除传递到 resultKey.

Anda boleh menggunakan atribut React.ComponentProps 实用程序类型来提取组件的 props 类型。然后,您可以使用 Omit 类型实用程序从组件的 props 中提取传递到 resultKey parameter.

type ComponentProps = React.ComponentProps
type NeededProps = Omit

Lihat ini jawapan untuk mendapatkan maklumat lanjut tentang mengekstrak jenis Prop komponen React daripada komponen itu sendiri.

Sebagai alternatif, jika anda ingin membuat kesimpulan jenis hasil Pertanyaan dan mengalih keluar sifat daripada prop berdasarkan jenis hasil tersebut, anda boleh menggunakan ResultType 实用程序类型和 keyof untuk melaksanakan fungsi:

type KeysOfDataReturnType = keyof ReturnType['data'];
type NeededProps = Omit;
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan