Реагирование на TypeScript высокого порядка компонентов - PullRequest
0 голосов
/ 03 марта 2019

Я пытаюсь обернуть существующий реактивный компонент (Reaction-Select) в Компонент высокого порядка (HoC), чтобы обеспечить некоторую логику условного рендеринга.Сложность, с которой я сталкиваюсь, заключается в получении TypeScript для генерации компонента, объединяющего свойства оболочки HoC и Wrapped.

Например:

import React from "react";
import Select from "react-select";

interface HOCProps { 
  foo: string
}

// Return a type with the HOCProps properties removed.
type WitoutPrefilled<T extends HOCProps> = Pick<T, Exclude<keyof T, 'foo'>>; 

function withHoC<P extends HOCProps>(WrappedComponent: React.ComponentType<P>) {
    return class SomeHOC extends React.Component<WithoutPrefilled<P>> {
        public render(): JSX.Element {
            return <WrappedComponent {...this.props as P} /*foo={"test"}*/ />;
        }
    };
}

// Generate a wrapped component with a property union of the wrapped Select and outer HoC (HOCProps & Select) ?
const Wrapped = withHoC(Select);

Как правильно это сделать?

Реагировать 16.8.3 TypeScript 3.3.3

Ответы [ 2 ]

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

Хорошо - так что мне удалось заставить это работать.Текущее решение удовлетворяет двум основным требованиям для меня:

  1. Выводит defaultProps для дочернего компонента, который оборачивается, если они присутствуют, то есть компонент, сгенерированный оболочкой HoC, не долженявно передать их.

  2. Предоставить объединение свойств оболочек HoC и свойств нижестоящих компонентов, чтобы Intellisense отображал все доступные свойства для обернутого компонента.

В моем стремлении упростить то, что происходит на самом деле, типы, ожидаемые функцией withHoC, жестко закодированы (в моем случае react-select), поэтому оболочка withHoC примет только react-selectSelect компонент для переноса.Все остальное, вероятно, приведет к ошибкам типа.

Эта ссылка описывает некоторый код, который может автоматически выводить тип компонента, который будет обернут withHoC, что делает withHoC повторно используемымс компонентами, отличными от react-select's Select.

// node dependencies used (because dependencies mutate so much this may not work in other versions):
// "react": "^16.8.2",
// "react-dom": "^16.8.2",
// "typescript": "^3.3.3",
// "react-select": "2.4.1",
// "@types/react": "^16.8.3",
// "@types/react-dom": "^16.8.2",
// "@types/react-select": "^2.0.13",

// Visual Studio 2017 TypeScript SDK build 3.3.3

import ReactDOM from "react-dom";   // (Optional - just for testing)
import React from "react";
import Select from "react-select";

// Properties shape for React Select (See react-select @type definitions)
import { Props } from "react-select/lib/Select";

// The properties we are want to add so that our resultant wrapped component contains all of its own properties plus the extra properties specified here
interface IHOCProps {
    bar: string;
}

function withHoC(WrappedComponent: React.ComponentType<Props>) {
    return class SomeHOC extends React.Component<IHOCProps & Props> {

        // If 'bar' isn't specified, configure a default (this section is optional)
        static defaultProps = {
            bar: "default bar"
        };

        public render(): JSX.Element {

            return <><div>{this.props.bar}</div><WrappedComponent {...this.props as any} /></>;
        }
    };
}

const WrappedSelect = withHoC(Select);

export { WrappedSelect, Select };

// Test it out (Optional). If using Visual Studio 2017 or some other IDE with intellisense, 
// <WrappedSelect /> should show all the 'react-select' properties and the HoC property (bar). 
// Additionally, all the defaultProps for 'react-select' are automatically inferred so no TypeScript errors about missing props when using <WrappedSelect />. 

const TestMe = () => 
<>
    <WrappedSelect bar="bumble monkey"> 
    <WrappedSelect />
    <Select />
</>;
// Append the result to an HTML document body element 
ReactDOM.render(<TestMe />,document.getElementsByTagName("body")[0]); 

В некоторой степени пиррова победа, поскольку она не может быть повторно использована разными типами, но она работает.

Один последний самородок;если вы используете Visual Studio 2017 и узел для TypeScript, убедитесь, что версия TypeScript SDK синхронизирована с пакетом узла npm, в противном случае среда IDE может сообщать об ошибках, которые не проявляются при компиляции командной строки (это не дало мне концапроблем с красной сельдью).

Microsoft опубликовала этот URL , который редко обновляется и, вероятно, устарел, но никто в корпорации не заметил.

Самый последний SDK всегда можно найти на GitHub здесь , который обычно всегда публикуется вместе с пакетами npm и nuget.

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

Во-первых, убедитесь, что ваш компонент высшего порядка поставляет реквизит, в котором заинтересован ваш компонент. Например, className.

interface WithClassName {
  className: string;
}

Наш withClassName HOC будет принимать компонент, готовый принять className в качестве реквизита и вернуть компонент, который больше не принимает className.

export function withClassName<T extends React.ComponentType<Partial<WithClassName>>>(Component: T): React.FunctionComponent<Omit<React.ComponentProps<T>, keyof WithClassName>> {
  return props => React.createElement(Component, { className: "foo", ...props });
}

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Использование:

const NewSelect = withClassName(Select);
...