Typescript + React:定义通用表组件

时间:2019-11-15 11:13:30

标签: reactjs typescript components typescript-generics

已经2天了,我一直在努力制作一个可重复使用的表,但是我遇到了类型的问题。
我不是打字稿/反应专家,因为我与javascript在一起工作更多,所以我想向您展示开发此组件的正确方法。

信息:

typescript: "3.6.4"
react: "^16.11.0"

"engines": {
 "node": "10.x",
 "npm": "6.x"
}

我的想法是(不完整且有类型错误):

import * as React from 'react';

const getCellData = (fields: Record<string, string>, row) => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return Object.entries(fields).reduce((res: Record<string, any>, val) => {
    res[val[0]] = row[val[1]];
    return res;
  }, {});
};

interface Props<C, R> {
  columns: C[];
  rows: R[];
}

const Table = <C, R>({ columns, rows }: Props<C, R>) => {
  return (
    <div className="table-responsive">
      <table className="table table-striped">
        <thead>
          <tr>
            {columns.map((column, i: number) => (
              <th scope="col" key={i}>
                {column.label}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows &&
            rows.map((row, j: number) => {
              return (
                <tr key={j}>
                  {columns.map((column, k: number) => {
                    const { Template, fields } = column;
                    const data = getCellData(fields, row);
                    return <td key={k}>{<Template {...data}></Template>}</td>;
                  })}
                </tr>
              );
            })}
        </tbody>
      </table>
    </div>
  );
};

export default Table;

我正在JSX中使用这样的表(像这样在jsx中传递类型正确吗?)

      <Table<SportsTableColumn, RedaxSport> columns={columns} rows={sports}></Table>

其中SportsTableColumn为:

export interface SportsTableColumn {
  label: string;
  Template: typeof TableTdText | typeof TableTdTextWithImage | typeof TableTdBoolCheck;
  fields: {
    text?: string;
    image?: string;
    checked?: string;
  };
}

TableTdText,TableTdTextWithImage,TableTdBoolCheck是React.FC

然后我们有RedaxSport,它是一个具有以下属性的类:

this.sportId = sportId;
this.urlIcon = urlIcon;
this.redaxDescription = redaxDescription;
this.aamsDescription = aamsDescription;
this.isDailyProgram = isDailyProgram;

“列”数组数据示例:

[
  {
    label: intl.formatMessage({ id: 'sports.table.redaxDescription' }),
    Template: TableTdTextWithImage,
    fields: {
      text: 'redaxDescription',
      image: 'urlIcon'
    }
  },
  {
    label: intl.formatMessage({ id: 'sports.table.aamsDescription' }),
    Template: TableTdText,
    fields: {
      text: 'aamsDescription'
    }
  },
  {
    label: intl.formatMessage({ id: 'sports.table.dailyProgram' }),
    Template: TableTdBoolCheck,
    fields: {
      checked: 'isDailyProgram'
    }
  }
];

“行”数据示例:

[
  {
      "sportId": 0,
      "urlIcon": "https://url.com/",
      "redaxDescription": "This is a description",
      "aamsDescription": "Another description",
      "isDailyProgram": true
  },
  {
      "sportId": 1,
      "urlIcon": "https://url2.com/",
      "redaxDescription": "This is a description 2",
      "aamsDescription": "Another description 2",
      "isDailyProgram": false
  }
]

我只是希望清楚我的意图,并为您提供所需的所有信息。 谢谢

2 个答案:

答案 0 :(得分:1)

U必须为您的列提供类型或接口,例如:

interface IColumn {
  label: string;
  fields: { [key: string]: string };
  Template: React.ComponentClass;
};

对表组件中的C泛型说,它接受扩展该接口的所有内容

const Table = <C extends IColumn, R>({ columns, rows }: Props<C, R>) => {...}

因此,这将使您的组件知道它可以使用IColumn界面中的任何字段。

然后用SportsTableColumn扩展IColumn

export interface SportsTableColumn extends IColumn {
  label: string;
  Template: React.ComponentClass;
  fields: {
    text?: string;
    image?: string;
    checked?: string;
  };
}

注意:将React.ComponentClass替换为TdComponents

答案 1 :(得分:0)

我将Template: React.ComponentClass;替换为Template: typeof TableTdText | typeof TableTdTextWithImage | typeof TableTdBoolCheck;
,但是我得到了
Type 'SportsTableColumn' does not satisfy the constraint 'Column'. Types of property 'Template' are incompatible. Type 'FunctionComponent<OwnProps> | FunctionComponent<OwnProps> | FunctionComponent<OwnProps>' is not assignable to type 'ComponentClass<{}, any>'. Type 'FunctionComponent<OwnProps>' is not assignable to type 'ComponentClass<{}, any>'. Type 'FunctionComponent<OwnProps>' provides no match for the signature 'new (props: {}, context?: any): Component<{}, any, any>'.

谢谢您的帮助!