代码之家  ›  专栏  ›  技术社区  ›  Joald

带react的typescript-在泛型组件类上使用hoc

  •  4
  • Joald  · 技术社区  · 7 年前

    我有一个通用的react组件,比如这个:

    class Foo<T> extends React.Component<FooProps<T>, FooState> {
        constructor(props: FooProps<T>) {
            super(props);
    
        render() {
            return <p> The result is {SomeGenericFunction<T>()}</p>;
        }
    }
    

    我也有一个hoc,看起来和这个类似(但没那么没意义):

    export const withTd = 
        <T extends WithTdProps>(TableElement: React.ComponentType<T>): React.SFC<T> => 
    (props: T) => <td><TableElement {...props}/></td>;
    

    但是当我使用这样的组件时:

    const FooWithTd = withTd(Foo);
    

    无法传递类型参数,因为您两个都不能 withTd(Foo<T>) ,你也做不到 FooWithTd ,类型总是错误的。 正确的方法是什么?

    编辑:问题是我想 <FooWithTd<number> {...someprops}/> 后来,因为我不知道 T 在临时办公室。

    3 回复  |  直到 7 年前
        1
  •  3
  •   Rico Kahler    7 年前

    谢谢你问这个问题。我只是想出了一种方法,用一个临时的组件来封装一个类型参数,我想我会分享它。

    import React from 'react';
    import withStyles from '@material-ui/core/styles/withStyles';
    import { RemoveProps } from '../helpers/typings';
    
    const styles = {
      // blah
    };
    
    interface Props<T> {
      classes: any;
      items: T[];
      getDisplayName: (t: T) => string;
      getKey: (t: T) => string;
      renderItem: (t: T) => React.ReactNode;
    }
    
    class GenericComponent<T> extends React.Component<Props<T>, State> {
      render() {
        const { classes, items, getKey, getDisplayName, renderItem } = this.props;
    
        return (
          <div className={classes.root}>
            {items.map(item => (
              <div className={classes.item} key={getKey(item)}>
                <div>{getDisplayName(item)}</div>
                <div>{renderItem(item)}</div>
              </div>
            ))}
          </div>
        );
      }
    }
    
    //   👇 create a `type` helper to that output the external props _after_ wrapping it
    type ExternalProps<T> = RemoveProps<Props<T>, 'classes'>;
    export default withStyles(
      styles
    )(GenericComponent) as <T extends any>(props: ExternalProps<T>) => any;
    //                       👆 cast the wrapped component as a function that takes
    //                          in a type parameter so we can use that type
    //                          parameter in `ExternalProps<T>`
    

    其主要思想是将包装好的组件转换为接受类型参数的函数(例如 T )并使用该类型参数导出外部道具。 之后 组件已被包装。

    如果这样做,则可以在使用包装版本时指定类型参数。 GenericComponent 例如。:

    <GenericComponent<string> {/*...*/} />
    

    希望代码对于那些仍然有这个问题的人来说足够解释了。不过,一般来说,我认为这是一种比较高级的类型化用法,使用起来可能更容易一些。 any 代替道具中的通用参数

        2
  •  2
  •   habsq    7 年前

    您可以将从hoc创建的组件包装到另一个组件中。它看起来像这样:

    class FooWithTd<T> extends React.Component<SomeType<T>> {
         private Container: React.Component<SomeType<T> & HOCResultType>; 
    
         constructor(props:SomeType<T>){
              super(props);
              this.Container = withTd(Foo<T>);
         }
    
         render() {
              return <this.Container {...this.props} />;
         }
    }
    

    请记住,您可能不希望在渲染函数中包含hoc,因为这意味着每个渲染都将重新创建组件。

        3
  •  0
  •   Stouffi    7 年前

    编辑: 在对代码进行了一些更改之后,它只是withtd函数中的一个错误约束t。

    // I needed to change the constraint on T, but you may adapt with your own needs
    export const withTd = <T extends FooProps<WithTdProps>>(
      TableElement: React.ComponentType<T>
    ): React.SFC<T> => (props: T) => (
      <td>
        <TableElement {...props} />
      </td>
    )
    
    // Explicitly typed constructor
    // Removed after EDIT
    //const FooW = Foo as new (props: FooProps<WithTdProps>) => Foo<WithTdProps>
    
    // Inferred as React.StatelessComponent<FooProps<WithTdProps>>
    const FooWithTd = withTd(Foo)
    

    编辑后不再相关:

    你可以在这个问题上找到更多的信息 https://github.com/Microsoft/TypeScript/issues/3960

        4
  •  0
  •   Chris    6 年前

    我也无意中发现了这一点,我想我最终会分享我的想法。

    基于Rigo Kaulle提供的方法,映射到代码的方法将是

    export const FooWithTd = withTd(Foo) as <T>(props: FooProps<T>) => React.ReactElement<FooProps<T>>;
    

    你可以这样用

    export class Bar extends React.Component<{}> {
      public render() {
        return (
          <FooWithTd<number> />
        );
      }
    }
    

    就我而言,我已经 defaultProps 另外,我通过另一种hoc方式注入道具,更完整的解决方案如下:

    type DefaultProps = "a" | "b";
    type InjectedProps = "classes" | "theme";
    type WithTdProps<T> = Omit<FooProps<T>, DefaultProps | InjectedProps> & Partial<FooProps<T> & { children: React.ReactNode }>;
    export const FooWithTd = withTd(Foo) as <T>(props: WithTdProps<T>) => React.ReactElement<WithTdProps<T>>;
    
    推荐文章