等)重新定义此组件。它应该能够表示不同语义的 HTML 元素,并且 TypeScript 自动调整以适应这些变化。
我们可以采用以下几种策略:
对于仅对一种元素创建抽象的组件,我们可以扩展该元素的属性。
对于想要定义不同元素的组件,我们可以创建多态组件。多态组件是设计为呈现为不同的 HTML 元素或组件,同时保持相同的属性和行为的组件。它允许我们指定一个 prop 来确定其渲染的元素类型。多态组件提供了灵活性和可重用性,而无需我们重新实现组件。举个具体的例子,你可以看看 Radix 的多态组件的实现。(https://www.radix-ui.com/primitives/docs/utilities/polymorphic)
在本教程中,我们将了解第一个策略。
镜像和扩展 HTML 元素的属性
让我们从简介中提到的第一个示例开始。我们想要创建一个带有适当样式的按钮,以便在我们的应用程序中使用。在 JavaScript 中,我们也许可以做这样的事情:
镜像和扩展 HTML 元素的属性
让我们从简介中提到的第一个示例开始。我们想要创建一个带有适当样式的按钮,以便在我们的应用程序中使用。在 JavaScript 中,我们也许可以做这样的事情:
const Button = (props) => {return ;
};
在 TypeScript 中,我们只需添加我们知道需要的内容即可。例如,我们知道 children 如果我们希望自定义按钮的行为与 HTML 按钮相同,则需要:
const Button = ({children}: React.PropsWithChildren) => {return {children} ;
};
您可以想象一次添加一个属性可能会有点乏味。相反,我们可以告诉 TypeScript 我们想要匹配它在 React 中用于元素的相同属性:
const Button = (props: React.ComponentProps) => {return ;
};
但我们有一个新问题。或者更确切地说,我们遇到了 JavaScript 示例中也存在的问题,但我们忽略了它。如果有人使用我们的新 Button 组件传递一个 classNameprop,它将覆盖我们的 className. 我们可以(并且我们将会)添加一些代码来处理这个问题,但我不想错过向您展示如何在 TypeScript 中使用实用程序类型的机会,以表示“我想使用所有来自 HTML 按钮的 props,除了一个(或多个)”:
type ButtonProps = Omit, 'className'>;
const Button = (props: ButtonProps) => {return ;
};
现在,TypeScript 将阻止我们或其他任何人将 className 属性传递到我们的 Button 组件中。如果我们只想使用传入的内容扩展类列表,我们可以通过几种不同的方式来实现。我们可以将其附加到列表中:
type ButtonProps = React.ComponentProps;
const Button = (props: ButtonProps) => {
const className = 'button' + props.className;
return ;
};
我喜欢在处理类时使用 clsx 库,因为它代表我们处理大多数此类事情:
import React from 'react';
import clsx from 'clsx';
type ButtonProps = React.ComponentProps;
const Button = ({className, ...props}: ButtonProps) => {return ;
};
export default Button;
我们学习了如何限制组件接受的 props。为了扩展 props,我们可以使用交集:
type ButtonProps = React.ComponentProps & {variant?: 'primary' | 'secondary';};
我们现在说的是 Button 接受元素接受的所有 props加上一个:variant。该道具将与我们继承的所有其他道具一起显示 HTMLButtonElement。
我们 Button 也可以添加对添加此类的支持:
const Button = ({variant, className, ...props}: ButtonProps) =/> {
return (
);
};
我们现在可以更新 src/application.tsx 以使用新的按钮组件:
diff --git a/src/application.tsx b/src/application.tsx
index 978a61d..fc8a416 100644
--- a/src/application.tsx
+++ b/src/application.tsx
@@ -1,3 +1,4 @@
+import Button from './components/button';
import useCount from './use-count';
const Counter = () => {@@ -8,15 +9,11 @@ const Counter = () => {
{count}
-
+
Decrement
-
-
- Reset
-
-
- Increment
-
+
+ Reset
+ Increment
您可以在本教程的 GitHub 存储库分支 中 button 找到上述更改。(https://github.com/stevekinney/polymorphic/tree/button)
创建复合组件
我通常最终为自己制作的另一个常见组件是分别使用正确的 for 和属性正确连接标签和输入元素的组件。id 我往往会厌倦一遍又一遍地输入以下内容:
Set Count
如果不扩展 HTML 元素的 props,我最终可能会根据需要慢慢添加 props:
type LabeledInputProps = {
id?: string;
label: string;
value: string | number;
type?: string;
className?: string;
onChange?: ChangeEventHandler;
};
正如我们在按钮中看到的那样,我们可以以类似的方式重构它:
type LabeledInputProps = React.ComponentProps & {label: string;};
除了 label 我们要传递给(呃)标签(我们经常希望将其与输入分组)之外,我们还手动将道具一一传递。我们要添加吗 autofocus?最好添加另一个道具。最好做这样的事情:
import {ComponentProps} from 'react';
type LabeledInputProps = ComponentProps & {label: string;};
const LabeledInput = ({id, label, ...props}: LabeledInputProps) => {
return ({label}
>
);
};
export default LabeledInput;
我们可以在以下位置交换新组件 src/application.tsx:
setValue(e.target.valueAsNumber)}
value={value}
/>
我们可以取出需要使用的东西,然后将其他所有东西传递给组件 ,然后在接下来的日子里假装它是一个标准 HTMLInputElement。
TypeScript 并不关心,因为 HTMLElement 它非常灵活,因为 DOM 早于 TypeScript。如果我们把一些完全令人震惊的东西扔进去,它只会抱怨。
您可以在本教程的 GitHub 存储库分支中 input 查看上述所有更改。(https://github.com/stevekinney/polymorphic/tree/input) 文章来源:https://www.toymoban.com/diary/js/424.html
文章来源地址 https://www.toymoban.com/diary/js/424.html
到此这篇关于在 TypeScript 中扩展 HTML 元素的属性的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!