当前位置: 首页 > 面试题库 >

使用Typescript进行反应—使用React.forwardRef时的泛型

濮阳研
2023-03-14
问题内容

我正在尝试创建一个通用组件,用户可以在其中将自定义传递OptionType给组件以一路进行类型检查。此组件还需要一个React.forwardRef

我可以在没有forwardRef的情况下使其正常工作。有任何想法吗?代码如下:

没有ForwardRef.tsx

export interface Option<OptionValueType = unknown> {
  value: OptionValueType;
  label: string;
}

interface WithoutForwardRefProps<OptionType> {
  onChange: (option: OptionType) => void;
  options: OptionType[];
}

export const WithoutForwardRef = <OptionType extends Option>(
  props: WithoutForwardRefProps<OptionType>,
) => {
  const { options, onChange } = props;
  return (
    <div>
      {options.map((opt) => {
        return (
          <div
            onClick={() => {
              onChange(opt);
            }}
          >
            {opt.label}
          </div>
        );
      })}
    </div>
  );
};

WithForwardRef.tsx

import { Option } from './WithoutForwardRef';

interface WithForwardRefProps<OptionType> {
  onChange: (option: OptionType) => void;
  options: OptionType[];
}

export const WithForwardRef = React.forwardRef(
  <OptionType extends Option>(
    props: WithForwardRefProps<OptionType>,
    ref?: React.Ref<HTMLDivElement>,
  ) => {
    const { options, onChange } = props;
    return (
      <div>
        {options.map((opt) => {
          return (
            <div
              onClick={() => {
                onChange(opt);
              }}
            >
              {opt.label}
            </div>
          );
        })}
      </div>
    );
  },
);

App.tsx

import { WithoutForwardRef, Option } from './WithoutForwardRef';
import { WithForwardRef } from './WithForwardRef';

interface CustomOption extends Option<number> {
  action: (value: number) => void;
}

const App: React.FC = () => {
  return (
    <div>
      <h3>Without Forward Ref</h3>
      <h4>Basic</h4>
      <WithoutForwardRef
        options={[{ value: 'test', label: 'Test' }, { value: 1, label: 'Test Two' }]}
        onChange={(option) => {
          // Does type inference on the type of value in the options
          console.log('BASIC', option);
        }}
      />
      <h4>Custom</h4>
      <WithoutForwardRef<CustomOption>
        options={[
          {
            value: 1,
            label: 'Test',
            action: (value) => {
              console.log('ACTION', value);
            },
          },
        ]}
        onChange={(option) => {
          // Intellisense works here
          option.action(option.value);
        }}
      />
      <h3>With Forward Ref</h3>
      <h4>Basic</h4>
      <WithForwardRef
        options={[{ value: 'test', label: 'Test' }, { value: 1, label: 'Test Two' }]}
        onChange={(option) => {
          // Does type inference on the type of value in the options
          console.log('BASIC', option);
        }}
      />
      <h4>Custom (WitForwardRef is not generic here)</h4>
      <WithForwardRef<CustomOption>
        options={[
          {
            value: 1,
            label: 'Test',
            action: (value) => {
              console.log('ACTION', value);
            },
          },
        ]}
        onChange={(option) => {
          // Intellisense SHOULD works here
          option.action(option.value);
        }}
      />
    </div>
  );
};

在中App.tsx,它表示该WithForwardRef组件不是通用的。有没有办法做到这一点?

回购示例:https :
//github.com/jgodi/generics-with-forward-
ref

谢谢!


问题答案:

创建通用组件作为输出React.forwardRef是不能直接1(见底部进一步信息)。不过,还有其他选择-让我们简化示例以进行说明:

type Option<O = unknown> = {
    value: O;
    label: string;
}
type Props<T extends Option<unknown>> = { options: T[] }

const options = [
  { value: 1, label: "la1", flag: true }, 
  { value: 2, label: "la2", flag: false }
] // just some options data

1.投放

// Given input comp for React.forwardRef
const FRefInputComp = <T extends Option>(props: Props<T>, ref: Ref<HTMLDivElement>) =>
    <div ref={ref}> {props.options.map(o => <p>{o.label}</p>)} </div>

// Cast the output
const FRefOutputComp1 = React.forwardRef(FRefInputComp) as
    <T extends Option>(p: Props<T> & { ref?: Ref<HTMLDivElement> }) => ReactElement

const Usage11 = () => <FRefOutputComp1 options={options} ref={myRef} />
// options has type { value: number; label: string; flag: boolean; }[] 
// , so we have made FRefOutputComp generic!

forwardRef之所以有效,是因为原则上返回类型是一个普通函数。我们只需要一个通用函数类型形状。您可以使用其他类型来简化断言:

type ForwardRefFn<R> = <P = {}>(p: P & React.RefAttributes<R>) => ReactElement | null
// RefAttributes is built-in with ref and key props defined
const Comp12 = React.forwardRef(FRefInputComp) as ForwardRefFn<HTMLDivElement>
const Usage12 = () => <Comp12 options={options} ref={myRef} />

2.包起来

const FRefOutputComp2 = React.forwardRef(FRefInputComp)
// T is replaced by its constraint Option<unknown> in FRefOutputComp2

export const Wrapper = <T extends Option>({myRef, ...rest}:
    Props<T> & {myRef: React.Ref<HTMLDivElement>}) => <FRefOutputComp2 {...rest} ref={myRef} />

const Usage2 = () => <Wrapper options={options} myRef={myRef} />

3.忽略它(使用自定义ref prop)

这是我最喜欢的-最简单的替代方法,在React中是合法的方法,并且不使用forwardRef

const Comp3 = <T extends Option>(props: Props<T> & { myRef: Ref<HTMLDivElement> }) =>
    <div ref={myRef}> {props.options.map(o => <p>{o.label}</p>)} </div>
const Usage3 = () => <Comp3 options={options} myRef={myRef} />

1的类型声明的React.forwardRef为:

function forwardRef<T, P = {}>(render: ForwardRefRenderFunction<T, P>): ...

因此,它采用了类似组件的通用接口,其类型参数T,P
必须 是具体的。换句话说:ForwardRefRenderFunction不是 一个普通的
函数
声明,所以高阶函数类型推断 不能
在调用函数传播自由型参数React.forwardRef

操场



 类似资料:
  • 我有一个类扩展了React。组件类,我将为我希望接收的道具传递我自己的接口。问题是当我使用webstorm linter说有问题,指出类型“typeof SomeComponent”上不存在属性“propTypes”。 我不知道为什么会这样...... 的代码为 当我编写以下代码创建函数组件时,会引发类似的异常: 在本例中,使用

  • 问题内容: 我开始研究ReactJS。看来Facebook刚刚发布了15.0.1版本。去年,我在使用JSXTransformer的0.12.x版本中研究了这个框架,现在看来它已不复存在。 现在看来,几乎每个教程都建议将最新的React与Webpack结合使用。有没有办法完全不使用webpack?我正在尝试使用React 15.xx的艰巨任务找到一个很好的有效示例 任何帮助,将不胜感激。 问题答案:

  • 问题内容: 我有以下签名的方法: 它读取json字符串,并将其转换为适当类型的对象的列表,例如。整数,字符串等 是否有一种可靠的方法可以通过从参数中推断出参数来将其从方法中删除?例如。有没有办法可以在不丢失功能的情况下将方法签名更改为以下内容? 似乎该解决方案将需要对ParameterizedType进行一些精美的操作。我看了以下内容,但是我使用的方法不正确,或者它们没有达到我的期望: 来自htt

  • 问题内容: 这个问题是使用TypeScript进行类类型检查的直接类比 我需要在运行时找出类型为any的变量是否实现了接口。这是我的代码: 如果在打字机游乐场输入此代码,则最后一行将被标记为错误,“名称A在当前作用域中不存在”。但这不是事实,该名称确实存在于当前范围内。我什至可以将变量声明更改为无编辑者的抱怨。浏览完网络并在SO上找到另一个问题后,我将接口更改为一个类,但是后来我无法使用对象文字来

  • 我正在从事一个Ionic/Angular项目,我正在尝试消除html文件中的类型检查问题。 我的. ts文件中有一个htest变量,如下所示 Typescript似乎对此很满意。我循环在我的html视图中显示它们,就像这样 但当我使用usr.value访问fn和ln时,typescript不理解它们是什么(红色下划线)。fn或usr.value。ln在html中,如上所述,带有keyvalue管道

  • 我无法理解为什么我的AppReact组件呈现两次,如下面的gif所示。 我插入了一个控制台。在返回组件之前记录日志,查看组件渲染的次数。 每当我移除useState钩子时,我的应用程序只会呈现一次我认为应该呈现的效果。任何关于为什么会发生这种情况的指导都是受欢迎的