react如何实现泛型组件

7,299 阅读2分钟

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性,主要在用typescript时可以实现类型推导,不需要人为的每个地方去约束类型。

函数:

function foo<T>(params: T): T {
  return params;
}

接口:

interface Params<T> {
  id: number
  name: string
  data: T[]
}

类:

class User<T> {
  id: number;
  data: T[];
  constructor() {
    this.id = 1;
    this.data = [];
  }
}

在实现react组件时,有时候我们也想通过泛型来定义某个属性的类型。比如常见的列表组件,如果不使用泛型,我们通常这样写:

//列表组件
interface Props {
  list: any[], //因为我们并不知道传进来的数组类型,只能用any代替
  renderItem: (item: any, i: number) => any
}
function MapList({ list = [], renderItem }) {
  return (
    <div>
      {list.map(renderItem)}
    </div>
  );
}

interface ListVO {
  id: number
  name: string
  amount: number
}

//存放列表组件的容器,即父组件
function TComponent = () => {
const list:ListVO[] = [
  { id: 1, name: "张一", amount: 1000 },
  { id: 2, name: "张二", amount: 2000 },
  { id: 3, name: "张三", amount: 3000 },
  { id: 4, name: "张四", amount: 4000 },
  { id: 5, name: "张五", amount: 5000 },
  { id: 6, name: "张六", amount: 6000 },
];
  return (
    <PageContainer title="泛型组件">
      <MapList 
        list={list}
        renderItem={(item) => (<p key={item.id}>{`${item.id}、${item.name}有¥${item.amount.toFixed(2)}`}</p>)}
      />
    </PageContainer>
  );
};

当我们使用组件MapList的renderItem属性时就不会有任何ts提示和类型约束,这违背了我们使用ts的初心,要么在使用renderItem的时候每次都自己再定义一下renderItem={(item: ListVO) => (...)},比较麻烦,这时候泛型就派上大用场了,我们只需要在声明子组件的时候为list声明一个泛型,如下:

interface Props<T> {
  list: T[],
  renderItem: (item: T, i: number) => any
}

function MapList<T>({ list = [], renderItem }: Props<T>) {
  return (
    <div>
      {list.map(renderItem)}
    </div>
  );
}

使用子组件,

<MapList<ListVO>
    list={list}
    renderItem={(item) => (...)}
/>

当然,因为ts的泛型可以根据自己推导类型,我们可以不用手动传递类型,

<MapList
    list={list}
    renderItem={(item) => (...)}
/>

看!这个时候ts的自动推导就实现啦~,你的组件可读性和维护性也提高一步咯~

image.png 如果你的组件是函数表达式,可以这样写,一样的,声明函数的不同泛型的写法有一丢丢不一样而已,

interface Props<T> {
  list: T[],
  renderItem: (item: T, i: number) => any
}

const MapList: <T>(props: Props<T>) => any = ({ list = [], renderItem }) => {
  return (
    <div>
      {list.map(renderItem)}
    </div>
  );
}