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

Juan Diego Rodriguez 2022年6月13日
使用 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