使用 TypeScript 管理 React 中的用户事件

Juan Diego Rodriguez 2024年2月16日
使用 TypeScript 管理 React 中的用户事件

本教程将展示如何在 React 中使用 TypeScript 管理用户的事件,方法是在用户的操作上从一个组件到另一个组件传递一个 onClick 函数。

使用 TypeScript 在 React 中管理用户事件

我们将使用 create-react-app 快速启动和运行一个新的 React 项目。

npx create-react-app my-app --template typescript
cd my-app
npm run start

安装必要的包并启动开发服务器后,我们将转到 src/App.tsx,删除所有样板代码并留下一个空组件。

import React from "react";

function Message() {
    return <div></div>;
}

export default Message;

现在我们将在 div 添加一个用户可以单击的按钮,我们将通过在 onClick 属性中传递一个带有警报的函数来响应。

function Message() {
    return (
        <div>
            <button
                onClick={() => {
                    alert("I was clicked!");
                }}>
                Click Me!
            </button>
        </div>
    );
}

从 Vanilla React 到 TypeScript 没有任何变化,但是一旦我们希望将 onClick 函数作为 Message 组件的 prop 传递,情况就不同了。为了展示这一点,我们将创建另一个名为 Game 的组件,它将 Message 作为其子组件。

function Game() {
    return (
        <div>
            <Message></Message>
        </div>
    );
}

export default Game;

我们将使 Message 接收它的 onClick 功能和 text 作为来自 Game 的道具。

function Message({onClick, text}) {
    return (
        <div>
            <button onClick={onClick}>{text}</button>
        </div>
    );
}

function Game() {
    return (
        <div>
            <Message
                onClick={() => {
                    alert("I was clicked!");
                }}
                text="Click me!"></Message>
        </div>
    );
}

但是,如果我们运行此代码,我们将得到以下编译错误。

Binding element 'onClick' implicitly has an 'any' type.
Binding element 'text' implicitly has an 'any' type.

在 Vanilla JavaScript 中,这不会导致错误,但 TypeScript 会抛出错误,因为 MessageonClicktext 道具隐含地具有 any 类型,即我们没有声明这些道具应该是哪种类型店铺。我们必须创建一个接口来指定 Message 的道具应该具有哪种类型来解决这个问题。

interface MessageProps {
    text: string;
    onClick: {};
}

text 属性应该具有的值很容易声明,因为它只是一个字符串。但是 onClick 的值更难。

onClick 不仅仅是一个常规函数,因为它具有 event 属性,并且它是 button 元素的预定属性。因此,要定义 onClick,我们需要一个 React 附带的预定义接口,在本例中称为 ButtonHTMLAttributes,它包含来自 button 元素的所有属性类型。

要使用它,我们必须扩展MessageProps 接口来存储 ButtonHTMLAttributes 类型。

interface MessageProps extends ButtonHTMLAttributes {
    text: string;
}

然而,这还不够,运行这样的代码会抛出一个错误,因为 ButtonHTMLAttributes 接口是一个 Generic Type。你可以将泛型类型视为带有变量的接口,为了使用它们,我们在声明接口后将它们包裹在 <> 周围。

在这种情况下,ButtonHTMLAttributes 接口需要一个变量来知道我们正在使用哪个 HTML 元素,它将是全局的 HTMLButtonElement

interface MessageProps extends ButtonHTMLAttributes<HTMLButtonElement> {
    text: string;
}

MessageProps 不仅包含 textonClick 道具的类型,还包含 button 元素的所有道具的类型。你可以将 button 中的任何道具添加到 Message

如果你只想扩展 onClick 属性,不要扩展界面,创建一个新的 onClick 类型并使用索引访问类型分配 ButtonHTMLAttributesonClick 属性。

interface MessageProps {
    text: string;
    onClick: ButtonHTMLAttributes<HTMLButtonElement>["onClick"];
}

最后,我们必须声明 Message 组件将通过以下方式将 MessageProps 用于其 props。

function Message({onClick, text}: MessageProps) {
    return (
        <div>
            <button onClick={onClick}>{text}</button>
        </div>
    );
}

如果我们愿意,我们可以将返回类型注释为 JSX.Element,这样如果我们不小心返回了其他类型,TypeScript 就会抛出错误。

function Message({onClick, text}: MessageProps): JSX.Element {
    return (
        <div>
            <button onClick={onClick}>{text}</button>
        </div>
    );
}

这将是最终的结果。

import React from "react";
import {ButtonHTMLAttributes} from "react";

interface MessageProps {
    text: string;
    onClick: ButtonHTMLAttributes<HTMLButtonElement>["onClick"];
}

function Message({onClick, text}: MessageProps): JSX.Element {
    return (
        <div>
            <button onClick={onClick}>{text}</button>
        </div>
    );
}

function Game() {
    return (
        <div>
            <Message
                onClick={() => {
                    alert("I was clicked!");
                }}
                text="Click me!"></Message>
        </div>
    );
}

export default Game;

代码示例

Juan Diego Rodriguez avatar Juan Diego Rodriguez avatar

Juan Diego Rodríguez (also known as Monknow) is a front-end developer from Venezuela who loves to stay updated with the latest web development trends, making beautiful websites with modern technologies. But also enjoys old-school development and likes building layouts with vanilla HTML and CSS to relax.

LinkedIn