Verwalten von Benutzerereignissen in React mit TypeScript

Juan Diego Rodriguez 16 Februar 2024
Verwalten von Benutzerereignissen in React mit TypeScript

Dieses Tutorial zeigt, wie Sie Benutzerereignisse in React mit TypeScript verwalten, indem Sie bei den Aktionen eines Benutzers eine onClick-Funktion von Komponente zu Komponente übergeben.

Verwalten von Benutzerereignissen in React mit TypeScript

Wir werden create-react-app verwenden, um schnell ein neues React-Projekt zu starten und auszuführen.

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

Nachdem wir die erforderlichen Pakete installiert und den Entwicklungsserver gestartet haben, gehen wir zu src/App.tsx, löschen den gesamten Boilerplate-Code und lassen eine leere Komponente.

import React from "react";

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

export default Message;

Jetzt fügen wir dem div eine Schaltfläche hinzu, auf die der Benutzer klicken kann, und wir antworten, indem wir eine Funktion mit einer Warnung innerhalb der onClick-Prop übergeben.

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

Nichts ändert sich von Vanilla React zu TypeScript, aber die Dinge sind anders, wenn wir wollen, dass die onClick-Funktion als Prop der Message-Komponente übergeben wird. Um dies zu zeigen, erstellen wir eine weitere Komponente namens Game, die Message als Kind hat.

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

export default Game;

Und wir werden Message dazu bringen, seine onClick-Funktion und text als Requisiten von Game zu erhalten.

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>
    );
}

Wir erhalten jedoch die folgenden Kompilierungsfehler, wenn wir diesen Code ausführen.

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

In Vanilla JavaScript verursacht dies keinen Fehler, aber TypeScript wirft einen, da die Props onClick und text von Message implizit einen any-Typ haben, d. h. wir haben nicht deklariert, welchen Typ diese Props haben sollten Laden. Wir müssen eine Schnittstelle erstellen, die angibt, welchen Typ die Requisiten der Nachricht haben sollen, um dies zu lösen.

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

Der Wert, den das Prop text haben soll, ist einfach zu deklarieren, da es nur ein String ist. Schwieriger ist aber der Wert für onClick.

onClick ist mehr als eine normale Funktion, da es eine event-Eigenschaft hat und eine vorgegebene Eigenschaft des button-Elements ist. Um also onClick zu definieren, benötigen wir eine vordefinierte Schnittstelle, die mit React geliefert wird, die in diesem Fall ButtonHTMLAttributes heißt und alle Eigenschaftstypen des button-Elements enthält.

Um es zu verwenden, müssen wir die Schnittstelle MessageProps erweitern, um die Typen ButtonHTMLAttributes zu speichern.

interface MessageProps extends ButtonHTMLAttributes {
    text: string;
}

Dies ist jedoch nicht genug, und das Ausführen des Codes wie diesem wird einen Fehler auslösen, da die Schnittstelle ButtonHTMLAttributes ein generischer Typ ist. Sie können sich generische Typen als Schnittstellen mit Variablen vorstellen, und um sie zu verwenden, wickeln wir sie um <>, nachdem wir die Schnittstelle deklariert haben.

In diesem Fall benötigt die Schnittstelle ButtonHTMLAttributes eine Variable, um zu wissen, welches HTML-Element wir verwenden, und es wird das globale HTMLButtonElement sein.

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

MessageProps enthält nicht nur die Typen für die Props text und onClick, sondern die Typen für alle Props eines button-Elements. Sie können jede Requisite von einem button zu Message hinzufügen.

Falls Sie nur das Prop onClick erweitern möchten, erweitern Sie die Schnittstelle nicht, erstellen einen neuen onClick-Typ und weisen die onClick-Eigenschaft der ButtonHTMLAttributes mit Indexed Access Types zu.

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

Abschließend müssen wir erklären, dass die Message-Komponente MessageProps für ihre Requisiten auf folgende Weise verwendet.

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

Und wenn wir wollen, können wir den Rückgabetyp als JSX.Element annotieren, sodass TypeScript einen Fehler auslöst, wenn wir versehentlich einen anderen Typ zurückgeben.

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

Und das wäre das Endergebnis.

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;

Codebeispiel

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