Аргументы общего типа в элементах JSX с withStyles - PullRequest
0 голосов
/ 29 сентября 2018

В React with material-ui я пытаюсь создать компонент JSX, который принимает универсальные параметры, а также использует withStyles HOC для внедрения моих стилей.

Первый подход был следующим:

const styles = (theme: Theme) => createStyles({
  card:    {
    ...
  }
});

interface Props<T> {
  prop: keyof T,
  ...
}

type PropsWithStyles<T> = Props<T> & WithStyles<typeof styles>;

export default withStyles(styles)(
    class BaseFormCard<T> extends React.Component<PropsWithStyles<T>> {
      ...
    }
),

Но при попытке использовать это универсальные типы теряются

<BaseFormCard<MyClass> prop={ /* no typings here */ } />

Единственное решение, которое я смог найти, - это обернуть экспорт в функцию, которая принимает универсальный параметр и создает компонент.

export default function WrappedBaseFormCard<T>(props: Props<T>): ReactElement<Props<T>> {

  const wrapper = withStyles(styles)(
        class BaseFormCard<T> extends React.Component<PropsWithStyles<T>> {
          ...
        }
    ) as any;

  return React.createElement(wrapper, props);
}

Однако это нелепо сложно и даже связано с затратами времени выполнения, хотя оно только пытается решить проблемы при наборе текста.

Должен быть лучший способ использования компонентов JSX собщие параметры и HOC.

Это тесно связано с проблемой здесь https://github.com/mui-org/material-ui/issues/11921,, но никогда не было удовлетворительного решения, и проблема теперь закрыта.

Ответы [ 3 ]

0 голосов
/ 30 сентября 2018

Чем больше я думаю об этом вопросе, тем больше мне нравится подход Фрэнка Ли .Я хотел бы сделать две модификации: (1) ввести дополнительный SFC, чтобы избежать приведения, и (2) извлечь внешний тип реквизита из обернутого компонента C вместо его жесткого кодирования.(Если мы жестко закодируем Props<T>, TypeScript по крайней мере проверит, что он совместим с this.C, но мы рискуем потребовать реквизиты, которые this.C на самом деле не требуют или не могутпринимать необязательные реквизиты, которые this.C на самом деле принимает.) Потрясающе то, что ссылка на тип свойства из аргумента типа в предложении extends работает, но, похоже,!

class WrappedBaseFormCard<T> extends React.Component<
  // Or `PropsOf<WrappedBaseFormCard<T>["C"]>` from @material-ui/core if you don't mind the dependency.
  WrappedBaseFormCard<T>["C"] extends React.ComponentType<infer P> ? P : never,
  {}> {
  private readonly C = withStyles(styles)(
    // JSX.LibraryManagedAttributes handles defaultProps, etc.  If you don't
    // need that, you can use `BaseFormCard<T>["props"]` or hard-code the props type.
    (props: JSX.LibraryManagedAttributes<typeof BaseFormCard, BaseFormCard<T>["props"]>) =>
      <BaseFormCard<T> {...props} />);
  render() {
    return <this.C {...this.props} />;
  }
}

Я думаю, что любые жалобызатраты времени выполнения этого подхода, вероятно, бессмысленны в контексте всего приложения React;Я поверю им, когда кто-то представит данные, подтверждающие их.

Обратите внимание, что подход Лукаса Зека с использованием SFC сильно отличается: каждый раз, когда изменяется реквизит внешнего SFC, и он вызывается снова, вызывается withStylesопять же, генерируя wrapper, который выглядит как React как новый тип компонента, поэтому React отбрасывает старый экземпляр wrapper, и создается новый внутренний компонент BaseFormCard.Это может иметь нежелательное поведение (состояние сброса), не говоря уже о больших накладных расходах во время выполнения.(Я на самом деле не проверял это, поэтому дайте мне знать, если я что-то упустил.)

0 голосов
/ 07 марта 2019

Вот решение, которое не создает компонент-оболочку.Вместо этого он создает тип, подобный типу компонента, только без свойства 'classes'.

   // TypeUtils.tsx
   // from: https://stackoverflow.com/questions/48215950/exclude-property-from-type
   export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

Используя этот вспомогательный тип, вы можете создать нужный тип и привести ваш стилизованный компонент к этому типу

   type FixedCheckBoxType = <T>(props: Omit<FormCheckBoxProps<T>, 'classes'>) => JSX.Element;
   export const FormCheckBox = withStyles(checkboxStyles)(UnstyledFormCheckBox) as FixedCheckBoxType;

Возможно, есть лучший способ сделать это, но в идеале это будет сделано автоматически самим material-ui.

0 голосов
/ 29 сентября 2018

Для каждого возможного числа параметров типа должна быть перегрузка withStyles печатаний

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...