如何在范型组件中使用 forwardRef

如何在范型组件中使用 forwardRef

English

在TypeScript中使用React的forwardRef时,存在一些限制,其中最大的限制是它禁用了对泛型组件的类型推断。

什么是泛型组件?

泛型组件的一个常见用途是Table组件:

tsx 复制代码
const Table = <T,>(props: {
  data: T[];
  renderRow: (row: T) => React.ReactNode;
}) => {
  return (
    <table>
      <tbody>
        {props.data.map((item, index) => (
          <props.renderRow key={index} {...item} />
        ))}
      </tbody>
    </table>
  );
};

这里,当我们传递一个数组到data时,它会推断出传递给renderRow函数的参数的类型。

tsx 复制代码
<Table
  {/* 1. Data is a string here... */}
  data={["a", "b"]}
  {/* 2. So ends up inferring as a string in renderRow. */}
  renderRow={(row) => {
    return <tr>{row}</tr>;
  }}
/>;
 
<Table
  {/* 3. Data is a number here... */}
  data={[1, 2]}
  {/* 4. So ends up inferring as a number in renderRow. */}
  renderRow={(row) => {
    return <tr>{row}</tr>;
  }}
/>;

这对于无需任何额外注解即可在renderRow函数上获得类型推断非常有帮助。

使用forwardRef的问题

问题出现在当我们尝试给我们的Table组件添加一个ref时:

tsx 复制代码
const Table = <T,>(
  props: {
    data: T[];
    renderRow: (row: T) => React.ReactNode;
  },
  ref: React.ForwardedRef<HTMLTableElement>
) => {
  return (
    <table ref={ref}>
      <tbody>
        {props.data.map((item, index) => (
          <props.renderRow key={index} {...item} />
        ))}
      </tbody>
    </table>
  );
};

const ForwardReffedTable = React.forwardRef(Table);

到目前为止,这一切看起来都很好,但是当我们使用我们的ForwardReffedTable组件时,之前看到的类型推断不再工作。

tsx 复制代码
<ForwardReffedTable
  {/* 1. Data is a string here... */}
  data={["a", "b"]}
  {/* 2. But ends up being inferred as unknown. */}
  renderRow={(row) => {
    return <tr />;
  }}
/>;
 
<ForwardReffedTable
  {/* 3. Data is a number here... */}
  data={[1, 2]}
  {/* 4. But still ends up being inferred as unknown. */}
  renderRow={(row) => {
    return <tr />;
  }}
/>;

这非常令人沮丧。但是,这个问题可以被解决。

解决方案

我们可以使用不同的类型定义来重新定义forwardRef,然后它就开始工作了。

这是新的定义:

ts 复制代码
function fixedForwardRef<T, P = {}>(
  render: (props: P, ref: React.Ref<T>) => React.ReactNode
): (props: P & React.RefAttributes<T>) => React.ReactNode {
  return React.forwardRef(render) as any;
}

我们可以改变我们的定义来使用fixedForwardRef

ts 复制代码
const ForwardReffedTable = fixedForwardRef(Table);

突然之间,它就开始工作了:

tsx 复制代码
<Table
  {/* 1. Data is a string here... */}
  data={["a", "b"]}
  {/* 2. So ends up inferring as a string in renderRow. */}
  renderRow={(row) => {
    return <tr>{row}</tr>;
  }}
/>;
 
<Table
  {/* 3. Data is a number here... */}
  data={[1, 2]}
  {/* 4. So ends up inferring as a number in renderRow. */}
  renderRow={(row) => {
    return <tr>{row}</tr>;
  }}
/>;

这是我的推荐解决方案 - 用实际工作的不同类型的新函数重新定义forwardRef。

Vue 3中的泛型组件示例

在Vue 3中,类型推断和引用的传递与React有所不同。Vue 3提供了Composition API,这是一种新的组件逻辑组织方式,它与TypeScript的集成让我们可以更灵活地处理类型推断和引用。以下是如何在Vue 3中处理类似问题的示例。

假设我们有一个类似的泛型Table组件,但在Vue 3的上下文中:

vue 复制代码
<template>
  <table>
    <tbody>
      <tr v-for="(item, index) in data" :key="index">
        <slot :row="item"></slot>
      </tr>
    </tbody>
  </table>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue';

export default defineComponent({
  props: {
    data: {
      type: Array as PropType<any[]>,
      required: true,
    },
  },
});
</script>

在这个组件中,我们使用了slot来代替React示例中的renderRow函数,这样父组件就可以决定如何渲染每一行。这种方法保留了类型推断的灵活性,并且适用于Vue的模板系统。

在Vue 3中,如果你想要传递一个引用(例如,到一个DOM元素或另一个组件实例),你可能会使用ref和响应式APIprovide/inject

vue 复制代码
<script setup lang="ts">
import { ref, provide } from 'vue';

const tableRef = ref(null);
provide('tableRef', tableRef);
</script>

然后在需要的地方注入引用:

vue 复制代码
<script setup lang="ts">
import { inject } from 'vue';

const tableRef = inject('tableRef');
</script>
相关推荐
秦jh_10 分钟前
【Linux】多线程(概念,控制)
linux·运维·前端
蜗牛快跑21323 分钟前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy24 分钟前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪1 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与2 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun2 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇2 小时前
ES6进阶知识一
前端·ecmascript·es6
渗透测试老鸟-九青2 小时前
通过投毒Bingbot索引挖掘必应中的存储型XSS
服务器·前端·javascript·安全·web安全·缓存·xss