美文网首页
React 泛型组件是什么?

React 泛型组件是什么?

作者: littleyu | 来源:发表于2023-06-20 10:37 被阅读0次

    案例

    假设我们要做一个很简单的 Show 组件,用法如下:

    <Show 
      content="hi" 
      onClick={c => console.log(c)}
    />
    

    Show 组件会展示 content,并在用户点击 content 时把 content 传给 onClick。

    因此代码中的 c 和 content 总是相等的。

    你会如何实现 Show 组件呢?假设你是这么写的:

    interface Props{
      content: any
      onClick?: (arg: any) => void
    }
    
    function Show(props: Props){
      const {content, onClick} = props
      return (
        <div>
          {content}
          <button 
            onClick={()=> onClick?.(content)}
          >
            Click me
          </button>
        </div>
      )
    }
    

    确实能完成功能,但是用到了两个 any,这不是 TypeScript,这是 AnyScript。

    改写

    于是你可以打算进行改写,你发现 content 和 onClick 的参数其实总是一样的,于是你写成这样:

    interface Props<T>{
      content: T
      onClick?: (arg: T) => void
    }
    

    但此时,Show 报错了:

    报错内容是 Props 需要接受一个参数 T,于是你让 Show 接受一个参数 T,再把 T 传给 Props:

    function Show<T>(props: Props<T>){ ...
    

    报错消除了。

    于是 Show 组件就可以这样用了:

    <Show<string> 
      content="hi" 
      onClick={c => console.log(c)}/>
    

    由于 TypeScript 有自动推断功能,所以可以从 content 自动推断出 T = string,所以可以简写成这样:

    <Show
      content="hi" 
      onClick={c => console.log(c)}/>
    

    这种写法跟最开始的写法一模一样,但是我们得到了一个大好处:c 的类型不再是 any 了,而是跟 content 保持一致:

    以上,就是泛型组件的简单演示。

    不过值得注意的是,上面的 Show 被我声明为普通函数,并没有被声明为 React.FunctionComponent,因为一旦声明成 React.FunctionComponent,就没法再加泛型了。

    另外,如果你喜欢用 Class 组件,那么可以把 Show 组件写成这样:

    class Show<T> extends Component<Props<T>> {
      render(){
        return (
        <div>
          {this.props.content}
          <button 
            onClick={
              ()=> this.props.onClick?.(this.props.content)}
          >
            Click me
          </button>
        </div>
        )
      }
    }
    
    export default function App() {
      return (
        <Show content='hi' onClick={c => console.log(c)} />
      );
    }
    

    实际场景

    在实际工作中,我们什么时候会遇到泛型组件呢?这里有一个典型的例子:Table 组件。

    假设我们的 Table 组件是这样用的:

    <Table
      data={[
        {id:1, name:'Alice',age:16,gender:'女'},
        {id:2, name:'Bob',age:16,gender:'男'},
        {id:3, name:'Carry',age:16,gender:'男'},
      ]}
      cols={[
        {key:'name', title:'姓名'},
        {key:'age', title:'年龄'}
      ]}
    />
    

    三条数据,只展示 name 和 age,我们如何确保 cols 里的 key 一定是 data 里面的字段名呢?

    用泛型即可:

      data: T[],
      cols: {
        key: keyof T // cols 的 key 必须是 data item 的字段名
        title: string
      }[]
    }
    
    function Table <T>(props: Props<T>) {
      const {data, cols} = props
      return (
        <table>
          {data.map(d => 
            <tr>
              {cols.map(c => 
                <td>{d[c.key]}</td>  
              )}
            </tr>
          )}
        </table>
      )
    }
    

    也就是说,当你需要两个 props 的类型有关联的时候,往往可以使用泛型组件来实现。

    不解

    不过目前我很疑惑下面这段代码翻译成 TypeScript 是怎样的:

    <Show<string> />
    

    虽然我知道这段代码翻译成 JavaScript 是

    React.createElement(Show)
    // 或
    _jsx(Show)
    

    但我并不知道翻译成 TypeScript 应该怎么写,尤其是 <string> 会被放在哪。

    如果你知道,请不吝赐教。

    相关文章

      网友评论

          本文标题:React 泛型组件是什么?

          本文链接:https://www.haomeiwen.com/subject/aggqydtx.html