如何包装一个内在组件,保留大部分 Props?

IT技术 reactjs typescript typescript-typings
2021-05-11 01:58:51

我想用我自己的 React 功能组件包装一个标准按钮,但我希​​望新组件的用户能够设置几乎所有底层按钮的props。当然,我想保持正确的输入,所以如果 WrappedButton 包含 a button,那么

<WrappedButton formNoValidate={true} onClick={handleClick} />

会正确编译,但

<WrappedButton formNoValidate={5} onKrapnik={handleClick} />

不会,因为它formNoValidate被输入为布尔值并且onKrapnik不存在。

显然,我不想列出底层组件的合法props的详尽(和脆弱)列表。

1个回答

原始组件有一些props。对于每个props,您可能想要

  • 原封不动地穿过它
  • 以某种方式修改
  • 省略它
  • 改变它的类型

您可能还想添加一些自己的新props。举个简单的例子,你只想添加一些新的props。我正在制作一种名为 的新型按钮MultiClick,它添加了一个可选的props:

type MultiClickProps = {
  clickCount?: number;
} & JSX.IntrinsicElements["button"];

const MultiClick: React.FC<MultiClickProps> = ({
  clickCount = 2,
  ...buttonProps
}) => {
// do some stuff
  return (
    <button {...buttonProps} />
  );
};

当然,这不是很有用:如果最终结果是返回一个按钮,所有props都未经修改,那么“某些东西”如何做任何有意义的事情?

所以你可能想拦截和修改一些props:

const MultiClick: React.FC<MultiClickProps> = ({
  clickCount = 2,
  onClick,
  ...buttonProps
}) => {
  const handleClick = (
    event: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    // do some stuff
    onClick(event);
  };

  return (
    <button {...buttonProps} onClick={handleClick}/>
  );
};

这样,就有机会与包装组件的操作进行实际交互。

现在假设您不喜欢 onClick 的“事件”参数,并且想要替换其他东西,一个数字。要更改类型,您实际上必须省略旧的 prop 并重新编写它,如下所示:

type MultiClickProps = {
  clickCount?: number;
  onClick: (n: number) => void;
} & Omit<JSX.IntrinsicElements["button"], 'onClick'>;

您可以在此处查看完成的代码

顺便说一下,这一切都取决于 JSX.IntrinsicElements 的存在,React 的人很友好地提供了它。假设您正在尝试从某个库中包装一个功能组件,该组件没有导出整洁的 props 类型?

幸运的是,您可以为任何用 Typescript 编写的功能组件生成自己的 props 类型,如下所示:

type PropsOf<T> =
    T extends React.FunctionComponent<infer U> ? U : never;

如果应用于上面的函数,就像这样:

type MultiClickProps = PropsOf<typeof MultiClick>;

将评估为类似

type MultiClickProps = {
    clickCount?: number;
} & React.ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement>