Einrichten von TypeScript für moderne React-Projekte mit Webpack

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Dieser Artikel stellt Typescript vor, ein hochgestelltes JavaScript, das die statische Typfunktion zum Erkennen häufiger Fehler als Entwicklercodes darstellt, was die Leistung verbessert und somit zu robusten Unternehmensanwendungen führt. Sie lernen auch, wie Sie TypeScript in einem React-Projekt effizient einrichten, während wir eine Money Heist Episode Picker-App erstellen und TypeScript, React-Hooks wie useReducer, useContext und Reach Router erkunden.

In dieser Ära der Softwareentwicklung kann JavaScript verwendet werden, um fast jede Art von App zu entwickeln. Die Tatsache, dass JavaScript dynamisch typisiert ist, könnte jedoch für die meisten großen Unternehmen ein Problem darstellen, da es eine lockere Typprüfungsfunktion enthält.

Glücklicherweise müssen wir nicht warten, bis das Ecma Technical Committee 39 ein statisches Typsystem in JavaScript einführt. Wir können stattdessen TypeScript verwenden.

Da JavaScript dynamisch typisiert ist, ist der Datentyp einer Variablen erst bekannt, wenn diese Variable zur Laufzeit instanziiert wird. Entwickler, die große Softwareprogramme schreiben, neigen möglicherweise dazu, eine zuvor deklarierte Variable einem Wert eines anderen Typs zuzuweisen, ohne dass eine Warnung oder ein Problem auftritt, was zu häufig übersehenen Fehlern führt.

In diesem Tutorial lernen wir, was TypeScript ist und wie man damit in einem React-Projekt arbeitet. Am Ende haben wir ein Projekt erstellt, das aus einer App zur Episodenauswahl für die TV-Show Haus des Geldes besteht, die TypeScript und aktuelle React-ähnliche Hooks ( useState , useEffect , useReducer , useContext ) verwendet. Mit diesem Wissen können Sie in Ihren eigenen Projekten mit TypeScript experimentieren.

Dieser Artikel ist keine Einführung in TypeScript. Daher gehen wir nicht auf die grundlegende Syntax von TypeScript und JavaScript ein. Sie müssen jedoch kein Experte in einer dieser Sprachen sein, um mitzumachen, denn wir werden versuchen, dem KISS-Prinzip zu folgen (keep it simple, stupid).

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Was ist TypeScript?

Im Jahr 2019 wurde TypeScript auf GitHub als die siebthäufigste Sprache und die am fünftschnellsten wachsende Sprache eingestuft. Aber was genau ist TypeScript?

Laut der offiziellen Dokumentation ist TypeScript eine typisierte Obermenge von JavaScript, die zu einfachem JavaScript kompiliert wird. Es wird von Microsoft und der Open-Source-Community entwickelt und gepflegt.

„Superset“ bedeutet in diesem Zusammenhang, dass die Sprache alle Merkmale und Funktionen von JavaScript enthält und noch einige mehr. TypeScript ist eine typisierte Skriptsprache.

Es bietet Entwicklern mehr Kontrolle über ihre Codebasis über seine Typannotation, Klassen und Schnittstelle und erspart Entwicklern die manuelle Behebung lästiger Fehler in der Konsole.

TypeScript wurde nicht erstellt, um JavaScript zu ändern. Stattdessen erweitert es JavaScript um wertvolle neue Funktionen. Jedes Programm, das in einfachem JavaScript geschrieben ist, wird auch wie erwartet in TypeScript ausgeführt, einschließlich plattformübergreifender mobiler Apps und Backends in Node.js.

Das bedeutet, dass Sie auch React-Apps in TypeScript schreiben können, wie wir es in diesem Tutorial tun werden.

Warum TypeScript?

Vielleicht sind Sie nicht davon überzeugt, die Vorteile von TypeScript anzunehmen. Betrachten wir einige seiner Vorteile.

Weniger Fehler

Wir können nicht alle Fehler in unserem Code beseitigen, aber wir können sie reduzieren. TypeScript prüft zur Kompilierzeit auf Typen und gibt Fehler aus, wenn sich der Variablentyp ändert.

In der Lage zu sein, diese offensichtlichen, aber häufigen Fehler so früh zu finden, macht es viel einfacher, Ihren Code mit Typen zu verwalten.

Umgestaltung ist einfacher

Sie möchten wahrscheinlich oft ziemlich viele Dinge umgestalten, aber weil sie so viel anderen Code und viele andere Dateien berühren, sind Sie vorsichtig, sie zu ändern.

In TypeScript können solche Dinge oft mit nur einem Klick auf den Befehl „Symbol umbenennen“ in Ihrer integrierten Entwicklungsumgebung (IDE) umgestaltet werden.

App in expApp umbenennen (große Vorschau)

In einer dynamisch typisierten Sprache wie JavaScript ist die einzige Möglichkeit, mehrere Dateien gleichzeitig umzugestalten, die traditionelle „Suchen und Ersetzen“-Funktion mit regulären Ausdrücken (RegExp).

In einer statisch typisierten Sprache wie TypeScript ist „Suchen und Ersetzen“ nicht mehr erforderlich. Mit IDE-Befehlen wie „Alle Vorkommen finden“ und „Symbol umbenennen“ können Sie alle Vorkommen der angegebenen Funktion, Klasse oder Eigenschaft einer Objektschnittstelle in der App anzeigen.

TypeScript hilft Ihnen dabei, alle Instanzen des umgestalteten Bits zu finden, es umzubenennen und Sie mit einem Kompilierungsfehler zu warnen, falls Ihr Code nach der Umgestaltung irgendwelche Typenkonflikte aufweist.

TypeScript hat noch mehr Vorteile als das, was wir hier behandelt haben.

Nachteile von TypeScript

TypeScript ist sicherlich nicht ohne Nachteile, selbst angesichts der oben hervorgehobenen vielversprechenden Funktionen.

Ein falsches Sicherheitsgefühl

Die Type-Checking-Funktion von TypeScript erzeugt bei Entwicklern oft ein falsches Sicherheitsgefühl. Die Typprüfung warnt uns tatsächlich, wenn etwas mit unserem Code nicht stimmt. Statische Typen reduzieren jedoch nicht die Gesamtfehlerdichte.

Daher hängt die Stärke Ihres Programms von Ihrer Verwendung von TypeScript ab, da Typen vom Entwickler geschrieben und nicht zur Laufzeit überprüft werden.

Wenn Sie TypeScript verwenden möchten, um Ihre Fehler zu reduzieren, sollten Sie stattdessen die testgetriebene Entwicklung in Betracht ziehen.

Kompliziertes Schreibsystem

Das Tippsystem ist zwar in vielerlei Hinsicht ein großartiges Werkzeug, kann aber manchmal etwas kompliziert sein. Dieser Nachteil ergibt sich aus der vollständigen Interoperabilität mit JavaScript, was noch mehr Raum für Komplikationen lässt.

TypeScript ist jedoch immer noch JavaScript, daher ist es wichtig, JavaScript zu verstehen.

Wann verwendet man TypeScript?

Ich würde Ihnen in folgenden Fällen zur Verwendung von TypeScript raten:

  • Wenn Sie eine Anwendung erstellen möchten, die über einen langen Zeitraum gewartet werden soll, würde ich dringend empfehlen, mit TypeScript zu beginnen, da es selbstdokumentierenden Code fördert und so anderen Entwicklern hilft, Ihren Code leicht zu verstehen, wenn sie Ihrer Codebasis beitreten .
  • Wenn Sie eine Bibliothek erstellen müssen, sollten Sie sie in TypeScript schreiben. Es hilft Code-Editoren, Entwicklern, die Ihre Bibliothek verwenden, die geeigneten Typen vorzuschlagen.

In den letzten Abschnitten haben wir die Vor- und Nachteile von TypeScript abgewogen. Kommen wir zum Tagesgeschäft: TypeScript in einem modernen React-Projekt einrichten .

Einstieg

Es gibt mehrere Möglichkeiten, TypeScript in einem React-Projekt einzurichten. In diesem Tutorial behandeln wir nur zwei.

Methode 1: React-App + TypeScript erstellen

Vor etwa zwei Jahren veröffentlichte das React-Team die Create React App 2.1 mit TypeScript-Unterstützung. Sie müssen sich also möglicherweise nie die Mühe machen, TypeScript in Ihr Projekt zu integrieren.

Ankündigung von TypeScript in der Create React App (große Vorschau)

Um ein neues Create React App-Projekt zu starten, können Sie Folgendes ausführen …

 npx create-react-app my-app --folder-name

… oder dieses:

 yarn create react-app my-app --folder-name

Um TypeScript zu einem Create React App-Projekt hinzuzufügen, installieren Sie es zuerst und die entsprechenden @types :

 npm install --save typescript @types/node @types/react @types/react-dom @types/jest

… oder:

 yarn add typescript @types/node @types/react @types/react-dom @types/jest

Benennen Sie als Nächstes die Dateien um (z. B. index.js in index.tsx ) und starten Sie Ihren Entwicklungsserver neu !

Das ging schnell, oder?

Methode 2: Richten Sie TypeScript mit Webpack ein

Webpack ist ein statischer Modul-Bundler für JavaScript-Anwendungen. Es nimmt den gesamten Code aus Ihrer Anwendung und macht ihn in einem Webbrowser nutzbar. Module sind wiederverwendbare Codeblöcke, die aus JavaScript, node_modules , Bildern und CSS-Stilen Ihrer App erstellt wurden und für die einfache Verwendung auf Ihrer Website gepackt sind.

Erstellen Sie ein neues Projekt

Beginnen wir damit, ein neues Verzeichnis für unser Projekt zu erstellen:

 mkdir react-webpack cd react-webpack

Wir werden npm verwenden, um unser Projekt zu initialisieren:

 npm init -y

Der obige Befehl generiert eine package.json -Datei mit einigen Standardwerten. Lassen Sie uns auch einige Abhängigkeiten für Webpack, TypeScript und einige React-spezifische Module hinzufügen.

Pakete installieren

Zuletzt müssen wir die erforderlichen Pakete installieren. Öffnen Sie Ihre Befehlszeilenschnittstelle (CLI) und führen Sie Folgendes aus:

 #Installing devDependencies npm install --save-dev @types/react @types/react-dom awesome-typescript-loader css-loader html-webpack-plugin mini-css-extract-plugin source-map-loader typescript webpack webpack-cli webpack-dev-server #installing Dependencies npm install react react-dom

Lassen Sie uns auch manuell einige verschiedene Dateien und Ordner unter unserem react-webpack Ordner hinzufügen:

  1. Fügen Sie webpack.config.js hinzu, um Webpack-bezogene Konfigurationen hinzuzufügen.
  2. Fügen Sie tsconfig.json für alle unsere TypeScript-Konfigurationen hinzu.
  3. Fügen Sie ein neues Verzeichnis hinzu, src .
  4. Erstellen Sie ein neues Verzeichnis, components , im src Ordner.
  5. Fügen Sie schließlich index.html , App.tsx und index.tsx im components hinzu.

Projektstruktur

Somit sieht unsere Ordnerstruktur in etwa so aus:

 ├── package.json ├── package-lock.json ├── tsconfig.json ├── webpack.config.js ├── .gitignore └── src └──components ├── App.tsx ├── index.tsx ├── index.html

Beginnen Sie mit dem Hinzufügen von Code

Wir beginnen mit index.html :

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>React-Webpack Setup</title> </head> <body> <div></div> </body> </html>

Dadurch wird der HTML-Code mit einem leeren div mit einer ID von output erstellt.

Fügen wir den Code unserer React-Komponente App.tsx :

 import * as React from "react"; export interface HelloWorldProps { userName: string; lang: string; } export const App = (props: HelloWorldProps) => ( <h1> Hi {props.userName} from React! Welcome to {props.lang}! </h1> );

Wir haben ein Schnittstellenobjekt erstellt und es HelloWorldProps genannt, wobei userName und lang vom Typ string sind.

Wir haben props an unsere App Komponente übergeben und exportiert.

Lassen Sie uns nun den Code in index.tsx :

 import * as React from "react"; import * as ReactDOM from "react-dom"; import { App } from "./App"; ReactDOM.render( <App userName="Beveloper" lang="TypeScript" />, document.getElementById("output") );

Wir haben gerade die App -Komponente in index.tsx importiert. Wenn Webpack eine Datei mit der Erweiterung .ts oder .tsx sieht, transpiliert es diese Datei mithilfe der awesome-typescript-loader-Bibliothek.

TypeScript-Konfiguration

Anschließend fügen wir tsconfig.json eine Konfiguration hinzu:

 { "compilerOptions": { "jsx": "react", "module": "commonjs", "noImplicitAny": true, "outDir": "./build/", "preserveConstEnums": true, "removeComments": true, "sourceMap": true, "target": "es5" }, "include": [ "src/components/index.tsx" ] }

Sehen wir uns auch die verschiedenen Optionen an, die wir zu tsconfig.json hinzugefügt haben:

  • compilerOptions Repräsentiert die verschiedenen Compileroptionen.
  • jsx:react Fügt Unterstützung für JSX in .tsx Dateien hinzu.
  • lib Fügt der Kompilierung eine Liste von Bibliotheksdateien hinzu (z. B. ermöglicht uns die Verwendung von es2015 die Verwendung von ECMAScript 6-Syntax).
  • module Generiert Modulcode.
  • noImplicitAny Fehler für Deklarationen mit einem implizierten any Typ aus.
  • outDir Repräsentiert das Ausgabeverzeichnis.
  • sourceMap Erzeugt eine .map -Datei, die zum Debuggen der App sehr nützlich sein kann.
  • target Stellt die ECMAScript-Zielversion dar, auf die unser Code heruntertranspiliert werden soll (wir können eine Version basierend auf unseren spezifischen Browseranforderungen hinzufügen).
  • include Wird verwendet, um die einzuschließende Dateiliste anzugeben.

Webpack-Konfiguration

Lassen Sie uns einige Webpack-Konfigurationen zu webpack.config.js .

 const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/components/index.tsx", target: "web", mode: "development", output: { path: path.resolve(\__dirname, "build"), filename: "bundle.js", }, resolve: { extensions: [".js", ".jsx", ".json", ".ts", ".tsx"], }, module: { rules: [ { test: /\.(ts|tsx)$/, loader: "awesome-typescript-loader", }, { enforce: "pre", test: /\.js$/, loader: "source-map-loader", }, { test: /\.css$/, loader: "css-loader", }, ], }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(\__dirname, "src", "components", "index.html"), }), new MiniCssExtractPlugin({ filename: "./src/yourfile.css", }), ], };

Schauen wir uns die verschiedenen Optionen an, die wir zu webpack.config.js hinzugefügt haben:

  • entry Dies gibt den Einstiegspunkt für unsere App an. Es kann sich um eine einzelne Datei oder ein Array von Dateien handeln, die wir in unseren Build aufnehmen möchten.
  • output Enthält die Ausgangskonfiguration. Die App sieht sich dies an, wenn sie versucht, gebündelten Code aus unserem Projekt auf die Festplatte auszugeben. Der Pfad stellt das Ausgabeverzeichnis für den auszugebenden Code dar, und der Dateiname stellt den Dateinamen für dasselbe dar. Es heißt im Allgemeinen bundle.js .
  • Auflösen resolve prüft dieses Attribut, um zu entscheiden, ob die Datei gebündelt oder übersprungen werden soll. Daher berücksichtigt webpack in unserem Projekt Dateien mit den Erweiterungen .js , .jsx , .json , .ts und .tsx für das Bündeln.
  • module Wir können es Webpack ermöglichen, eine bestimmte Datei zu laden, wenn sie von der App angefordert wird, indem wir Loader verwenden. Es nimmt ein Regelobjekt an, das Folgendes angibt:
    • Jede Datei, die mit der Erweiterung .tsx oder .ts endet, sollte zum Laden den awesome-typescript-loader verwenden;
    • Dateien, die mit der Erweiterung .js , sollten mit source-map-loader geladen werden;
    • Dateien, die mit der Erweiterung .css enden, sollten mit css-loader geladen werden.
  • plugins Webpack hat seine eigenen Einschränkungen und bietet Plugins, um diese zu überwinden und seine Fähigkeiten zu erweitern. Beispielsweise erstellt html-webpack-plugin eine Vorlagendatei, die für den Browser aus der Datei index.html im Verzeichnis ./src/component/index.html wird.

MiniCssExtractPlugin rendert die übergeordnete CSS -Datei der App.

Hinzufügen von Skripts zu package.json

Wir können verschiedene Skripte hinzufügen, um React-Apps in unserer Datei package.json erstellen:

 "scripts": { "start": "webpack-dev-server --open", "build": "webpack" },

Führen Sie nun npm start in Ihrer CLI aus. Wenn alles gut gelaufen ist, sollten Sie Folgendes sehen:

React-Webpack-Setup-Ausgabe (große Vorschau)

Wenn Sie ein Händchen für Webpacks haben, klonen Sie das Repository für dieses Setup und verwenden Sie es in Ihren Projekten.

Dateien erstellen

Erstellen Sie einen src -Ordner und eine index.tsx -Datei. Dies ist die Basisdatei, die React rendert.

Wenn wir jetzt npm start ausführen, wird unser Server ausgeführt und ein neuer Tab geöffnet. Wenn Sie npm run build wird das Webpack für die Produktion erstellt und ein Build-Ordner für uns erstellt.

Wir haben gesehen, wie man TypeScript mit der Create React App und der Webpack-Konfigurationsmethode von Grund auf neu einrichtet.

Eine der schnellsten Möglichkeiten, TypeScript vollständig zu verstehen, besteht darin, eines Ihrer vorhandenen Vanilla-React-Projekte in TypeScript zu konvertieren. Leider ist die schrittweise Übernahme von TypeScript in ein bestehendes Vanilla-React-Projekt stressig, da es dazu führt, dass alle Dateien ausgeworfen oder umbenannt werden müssen, was zu Konflikten und einem riesigen Pull-Request führen würde, wenn das Projekt zu einem großen Team gehört.

Als Nächstes sehen wir uns an, wie Sie ein React-Projekt einfach zu TypeScript migrieren können.

Migrieren Sie eine vorhandene Create React-App zu TypeScript

Um diesen Prozess überschaubarer zu machen, werden wir ihn in Schritte unterteilen, die es uns ermöglichen, in einzelnen Abschnitten zu migrieren. Hier sind die Schritte, die wir unternehmen werden, um unser Projekt zu migrieren:

  1. Fügen Sie TypeScript und Typen hinzu.
  2. Fügen Sie tsconfig.json .
  3. Fangen Sie klein an.
  4. Benennen Sie die Dateierweiterung in .tsx um.

1. Fügen Sie TypeScript zum Projekt hinzu

Zuerst müssen wir TypeScript zu unserem Projekt hinzufügen. Angenommen, Ihr React-Projekt wurde mit Create React App gebootstrapped, können wir Folgendes ausführen:

 # Using npm npm install --save typescript @types/node @types/react @types/react-dom @types/jest # Using Yarn yarn add typescript @types/node @types/react @types/react-dom @types/jest

Beachten Sie, dass wir noch nichts an TypeScript geändert haben. Wenn wir den Befehl ausführen, um das Projekt lokal zu starten ( npm start oder yarn start ), ändert sich nichts. Wenn dem so ist, dann super! Wir sind bereit für den nächsten Schritt.

2. Fügen Sie die Datei tsconfig.json

Bevor wir TypeScript nutzen können, müssen wir es über die Datei tsconfig.json konfigurieren. Der einfachste Weg, um anzufangen, besteht darin, mit diesem Befehl ein Gerüst zu erstellen:

 npx tsc --init

Dies verschafft uns einige Grundlagen mit viel kommentiertem Code. Ersetzen Sie nun den gesamten Code in tsconfig.json durch diesen:

 { "compilerOptions": { "jsx": "react", "module": "commonjs", "noImplicitAny": true, "outDir": "./build/", "preserveConstEnums": true, "removeComments": true, "sourceMap": true, "target": "es5" }, "include": [ "./src/**/**/\*" ] }

TypeScript-Konfiguration

Sehen wir uns auch die verschiedenen Optionen an, die wir zu tsconfig.json hinzugefügt haben:

  • compilerOptions Repräsentiert die verschiedenen Compileroptionen.
    • target Übersetzt neuere JavaScript-Konstrukte in eine ältere Version wie ECMAScript 5.
    • lib Fügt der Kompilierung eine Liste von Bibliotheksdateien hinzu (z. B. ermöglicht uns die Verwendung von es2015 die Verwendung von ECMAScript 6-Syntax).
    • jsx:react Fügt Unterstützung für JSX in .tsx Dateien hinzu.
    • lib Fügt der Kompilierung eine Liste von Bibliotheksdateien hinzu (z. B. ermöglicht uns die Verwendung von es2015 die Verwendung von ECMAScript 6-Syntax).
    • module Generiert Modulcode.
    • noImplicitAny Wird verwendet, um Fehler für Deklarationen mit einem impliziten any Typ auszulösen.
    • outDir Repräsentiert das Ausgabeverzeichnis.
    • sourceMap Erzeugt eine .map -Datei, die zum Debuggen unserer App sehr nützlich sein kann.
    • include Wird verwendet, um die einzuschließende Dateiliste anzugeben.

Die Konfigurationsoptionen variieren je nach Bedarf eines Projekts. Möglicherweise müssen Sie die Tabelle mit den TypeScript-Optionen überprüfen, um herauszufinden, was zu Ihrem Projekt passt.

Wir haben nur die erforderlichen Maßnahmen ergriffen, um die Dinge vorzubereiten. Unser nächster Schritt besteht darin, eine Datei zu TypeScript zu migrieren.

3. Beginnen Sie mit einer einfachen Komponente

Nutzen Sie die Fähigkeit von TypeScript, schrittweise übernommen zu werden. Gehen Sie eine Datei nach der anderen in Ihrem eigenen Tempo vor. Tun Sie, was für Sie und Ihr Team sinnvoll ist. Versuchen Sie nicht, alles auf einmal anzugehen.

Um dies richtig umzuwandeln, müssen wir zwei Dinge tun:

  1. Ändern Sie die Dateierweiterung in .tsx .
  2. Fügen Sie die Typanmerkung hinzu (was einige TypeScript-Kenntnisse erfordern würde).

4. Dateierweiterungen in .tsx

In einer großen Codebasis kann es ermüdend erscheinen, Dateien einzeln umzubenennen.

Benennen Sie mehrere Dateien unter macOS um

Das Umbenennen mehrerer Dateien kann eine Zeitverschwendung sein. So können Sie es auf einem Mac machen. Klicken Sie mit der rechten Maustaste (oder Ctrl + Klick oder klicken Sie mit zwei Fingern gleichzeitig auf dem Trackpad, wenn Sie ein MacBook verwenden) auf den Ordner, der die Dateien enthält, die Sie umbenennen möchten. Klicken Sie dann auf „Im Finder anzeigen“. Wählen Sie im Finder alle Dateien aus, die Sie umbenennen möchten. Klicken Sie mit der rechten Maustaste auf die ausgewählten Dateien und wählen Sie „X Elemente umbenennen…“. Dann sehen Sie so etwas:

Dateien auf einem Mac umbenennen (große Vorschau)

Geben Sie die Zeichenfolge ein, die Sie finden möchten, und die Zeichenfolge, durch die Sie diese gefundene Zeichenfolge ersetzen möchten, und klicken Sie auf „Umbenennen“. Getan.

Benennen Sie mehrere Dateien unter Windows um

Das Umbenennen mehrerer Dateien unter Windows würde den Rahmen dieses Tutorials sprengen, aber eine vollständige Anleitung ist verfügbar. Normalerweise würden Sie nach dem Umbenennen der Dateien Fehler erhalten; Sie müssen nur die Typanmerkungen hinzufügen. Sie können dies in der Dokumentation auffrischen.

Wir haben behandelt, wie man TypeScript in einer React-App einrichtet. Lassen Sie uns nun mit TypeScript eine Episodenauswahl-App für Money Heist erstellen.

Wir werden die grundlegenden Typen von TypeScript nicht behandeln. Es ist erforderlich, die Dokumentation durchzugehen, bevor Sie mit diesem Lernprogramm fortfahren.

Zeit zum Bauen

Damit sich dieser Prozess weniger entmutigend anfühlt, werden wir ihn in Schritte unterteilen, die es uns ermöglichen, die App in einzelnen Abschnitten zu erstellen. Hier sind alle Schritte, die wir unternehmen werden, um die Episodenauswahl für Haus des Geldes zu erstellen:

  • Erstellen Sie ein Gerüst für eine Create React App.
  • Folgen abrufen.
    • Erstellen Sie die entsprechenden Typen und Schnittstellen für unsere Episoden in interface.ts .
    • Richten Sie den Store zum Abrufen von Episoden in store.tsx .
    • Erstellen Sie die Aktion zum Abrufen von Episoden in action.ts .
    • Erstellen Sie eine EpisodeList.tsx -Komponente, die die abgerufenen Episoden enthält.
    • Importieren Sie die EpisodesList -Komponente mit React Lazy and Suspense auf unsere Homepage.
  • Folgen hinzufügen.
    • Store einrichten, um Episoden in store.tsx .
    • Erstellen Sie die Aktion zum Hinzufügen von Episoden in action.ts .
  • Folgen entfernen.
    • Richten Sie den Store zum Löschen von Episoden in store.tsx .
    • Erstellen Sie die Aktion zum Löschen von Folgen in action.ts .
  • Lieblingsfolge.
    • Importieren Sie die EpisodesList Komponente in die Lieblingsepisode.
    • Render EpisodesList innerhalb der Lieblingsepisode.
  • Verwenden von Reach Router für die Navigation.

Reaktion einrichten

Der einfachste Weg, React einzurichten, ist die Verwendung der Create React App. Create React App ist eine offiziell unterstützte Möglichkeit, einseitige React-Anwendungen zu erstellen. Es bietet ein modernes Build-Setup ohne Konfiguration.

Wir werden es verwenden, um die Anwendung zu booten, die wir erstellen werden. Führen Sie in Ihrer CLI den folgenden Befehl aus:

 npx create-react-app react-ts-app && cd react-ts-app

Starten Sie nach erfolgreicher Installation den React-Server, indem Sie npm start .

Startseite reagieren (große Vorschau)

Schnittstellen und Typen in Typoskript verstehen

Schnittstellen in TypeScript werden verwendet, wenn wir Objekteigenschaften Typen zuweisen müssen. Daher würden wir Schnittstellen verwenden, um unsere Typen zu definieren.

 interface Employee { name: string, role: string salary: number } const bestEmployee: Employee= { name: 'John Doe', role: 'IOS Developer', salary: '$8500' //notice we are using a string }

Beim Kompilieren des obigen Codes würden wir diesen Fehler sehen: „Arten des salary sind nicht kompatibel. Der Typ string ist dem Typ number nicht zuweisbar.“

Solche Fehler treten in TypeScript auf, wenn einer Eigenschaft oder Variablen ein anderer als der definierte Typ zugewiesen wird. Das obige Snippet bedeutet insbesondere, dass der salary ein string anstelle eines number zugewiesen wurde.

Lassen Sie uns eine interface.ts -Datei in unserem src -Ordner erstellen. Kopieren Sie diesen Code und fügen Sie ihn ein:

 /** |-------------------------------------------------- | All the interfaces! |-------------------------------------------------- */ export interface IEpisode { airdate: string airstamp: string airtime: string id: number image: { medium: string; original: string } name: string number: number runtime: number season: number summary: string url: string } export interface IState { episodes: Array<IEpisode> favourites: Array<IEpisode> } export interface IAction { type: string payload: Array<IEpisode> | any } export type Dispatch = React.Dispatch<IAction> export type FavAction = ( state: IState, dispatch: Dispatch, episode: IEpisode ) => IAction export interface IEpisodeProps { episodes: Array<IEpisode> store: { state: IState; dispatch: Dispatch } toggleFavAction: FavAction favourites: Array<IEpisode> } export interface IProps { episodes: Array<IEpisode> store: { state: IState; dispatch: Dispatch } toggleFavAction: FavAction favourites: Array<IEpisode> }

Es hat sich bewährt, dem Namen der Schnittstelle ein „I“ hinzuzufügen. Es macht den Code lesbar. Sie können sich jedoch entscheiden, es auszuschließen.

IEpisode-Schnittstelle

Unsere API gibt eine Reihe von Eigenschaften wie airstamp airdate airtime , id , image , name , number , runtime , season , summary und url zurück . Daher haben wir eine IEpisode -Schnittstelle definiert und die entsprechenden Datentypen auf die Objekteigenschaften gesetzt.

IState-Schnittstelle

Unsere IState Schnittstelle hat episodes bzw. favorites und eine Array<IEpisode> -Schnittstelle.

Aktion

Die Eigenschaften der IAction Schnittstelle sind payload und type . Die type Eigenschaft hat einen String-Typ, während die Payload einen Array | any -Typ hat Array | any .

Beachten Sie, dass Array | any Array | any bedeutet ein Array der Episodenschnittstelle oder einen beliebigen Typ.

Der Dispatch -Typ ist auf React.Dispatch und eine <IAction> -Schnittstelle festgelegt. Beachten Sie, dass React.Dispatch gemäß der @types/react -Codebasis der Standardtyp für die dispatch -Funktion ist, während <IAction> ein Array der Interface-Aktion ist.

Außerdem verfügt Visual Studio Code über einen TypeScript-Checker. Durch bloßes Hervorheben oder Bewegen des Mauszeigers über Code ist es intelligent genug, um den entsprechenden Typ vorzuschlagen.

Mit anderen Worten, damit wir unsere Schnittstelle über unsere Apps hinweg nutzen können, müssen wir sie exportieren. Bisher haben wir unseren Speicher und unsere Schnittstellen, die den Typ unseres Objekts enthalten. Lassen Sie uns jetzt unseren Shop erstellen. Beachten Sie, dass die anderen Schnittstellen denselben Konventionen wie die erläuterten folgen.

Folgen abrufen

Erstellen eines Shops

Um unsere Episoden abzurufen, benötigen wir einen Speicher, der den Anfangszustand der Daten enthält und der unsere Reducer-Funktion definiert.

Wir verwenden den useReducer Hook, um das einzurichten. Erstellen Sie eine store.tsx -Datei in Ihrem src -Ordner. Kopieren Sie den folgenden Code und fügen Sie ihn ein.

 import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext (initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload } default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return {children} } import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext (initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload } default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return {children} } import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext (initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload } default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return {children} } import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext (initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload } default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return {children} }

Im Folgenden sind die Schritte aufgeführt, die wir zum Erstellen des Shops unternommen haben:

  • Bei der Definition unseres Shops benötigen wir den useReducer Hook und die createContext API von React, weshalb wir sie importiert haben.
  • Wir IState und IAction aus ./types/interfaces importiert.
  • Wir haben ein initialState -Objekt mit dem Typ IState und Eigenschaften von Episoden und Favoriten deklariert, die beide jeweils auf ein leeres Array gesetzt sind.
  • Als Nächstes haben wir eine Store -Variable erstellt, die die Methode createContext und an die initialState .

Der Methodentyp createContext ist <IState | any> <IState | any> , was bedeutet, dass es sich um einen Typ von <IState> oder any handeln kann. Wir werden den any Typ sehen, der in diesem Artikel häufig verwendet wird.

  • Als nächstes haben wir eine reducer -Funktion deklariert und state und action als Parameter übergeben. Die reducer -Funktion hat eine switch-Anweisung, die den Wert von action.type . Wenn der Wert FETCH_DATA ist, wird ein Objekt zurückgegeben, das eine Kopie unseres Status (...state) und des Episodenstatus enthält, der unsere Aktionsnutzdaten enthält.
  • In der switch-Anweisung geben wir einen state of default zurück.

Beachten Sie, dass die state und action in der Reducer-Funktion IState bzw. IAction Typen haben. Außerdem hat die reducer -Funktion einen Typ von IState .

  • Zuletzt haben wir eine StoreProvider Funktion deklariert. Dadurch erhalten alle Komponenten in unserer App Zugriff auf den Store.
  • Diese Funktion nimmt children als Requisite, und innerhalb der StorePrivder Funktion haben wir den useReducer Hook deklariert.
  • Wir haben state und dispatch destrukturiert.
  • Um unseren Shop für alle Komponenten zugänglich zu machen, haben wir einen Objektwert mit state und dispatch übergeben.

Der state , der unsere Episoden und Favoriten enthält, wird von anderen Komponenten zugänglich gemacht, während der dispatch eine Funktion ist, die den Status ändert.

  • Wir werden Store und StoreProvider , damit sie in unserer gesamten Anwendung verwendet werden können.

Aktion.ts erstellen

Wir müssen Anfragen an die API stellen, um die Episoden abzurufen, die dem Benutzer gezeigt werden. Dies geschieht in einer Aktionsdatei. Erstellen Sie eine Action.ts -Datei und fügen Sie dann den folgenden Code ein:

 import { Dispatch } from './interface/interfaces' export const fetchDataAction = async (dispatch: Dispatch) => { const URL = 'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes' const data = await fetch(URL) const dataJSON = await data.json() return dispatch({ type: 'FETCH_DATA', payload: dataJSON.\_embedded.episodes }) }

Zuerst müssen wir unsere Schnittstellen importieren, damit sie in dieser Datei verwendet werden können. Folgende Schritte wurden unternommen, um die Aktion zu erstellen:

  • Die Funktion fetchDataAction nimmt dispatch -Requisiten als Parameter.
  • Da unsere Funktion asynchron ist, würden wir async und await verwenden.
  • Wir erstellen eine Variable ( URL ), die unseren API-Endpunkt enthält.
  • Wir haben eine weitere Variable namens data , die die Antwort von der API enthält.
  • Dann speichern wir die JSON-Antwort in dataJSON , nachdem wir die Antwort im JSON-Format durch den Aufruf von data.json() haben.
  • Zuletzt geben wir eine Dispatch-Funktion zurück, die eine Eigenschaft vom type und eine Zeichenfolge von FETCH_DATA . Es hat auch eine payload() . _embedded.episodes ist das Array des Episodenobjekts von unserem endpoint .

Beachten Sie, dass die Funktion fetchDataAction unseren Endpunkt abruft, ihn in JSON -Objekte konvertiert und die Dispatch-Funktion zurückgibt, die den zuvor im Store deklarierten Status aktualisiert.

Der exportierte Versandtyp ist auf React.Dispatch . Beachten Sie, dass React.Dispatch der Standardtyp für die Dispatch-Funktion gemäß der @types/react -Codebasis ist, während <IAction> ein Array der Schnittstellenaktion ist.

EpisodesList-Komponente

Um die Wiederverwendbarkeit unserer App zu gewährleisten, speichern wir alle abgerufenen Episoden in einer separaten Datei und importieren die Datei dann in unsere homePage Komponente.

Erstellen Sie im components eine EpisodesList.tsx -Datei, kopieren Sie den folgenden Code und fügen Sie ihn ein:

 import React from 'react' import { IEpisode, IProps } from '../types/interfaces' const EpisodesList = (props: IProps): Array<JSX.Element> => { const { episodes } = props return episodes.map((episode: IEpisode) => { return ( <section key={episode.id} className='episode-box'> <img src={!!episode.image ? episode.image.medium : ''} alt={`Money Heist ${episode.name}`} /> <div>{episode.name}</div> <section style={{ display: 'flex', justifyContent: 'space-between' }}> <div> Season: {episode.season} Number: {episode.number} </div> <button type='button' > Fav </button> </section> </section> ) }) } export default EpisodesList
  • Wir importieren IEpisode und IProps aus interfaces.tsx .
  • Als Nächstes erstellen wir eine EpisodesList -Funktion, die Requisiten verwendet. Die Requisiten haben den Typ IProps , während die Funktion den Typ Array<JSX.Element> hat.

Visual Studio Code schlägt vor, dass unser Funktionstyp als JSX.Element[] geschrieben wird.

Visual Studio Code schlägt einen Typ vor (große Vorschau)

Während Array<JSX.Element> gleich JSX.Element[] ist, wird Array<JSX.Element> als generische Identität bezeichnet. Daher wird das generische Muster in diesem Artikel häufig verwendet.

  • Innerhalb der Funktion destrukturieren wir die episodes von props , die IEpisode als Typ hat.

Lesen Sie mehr über die generische Identität. Dieses Wissen wird im weiteren Verlauf benötigt.

  • Wir haben die Requisiten der episodes zurückgegeben und durchgemappt, um ein paar HTML-Tags zurückzugeben.
  • Der erste Abschnitt enthält den key episode.id und einen className von episode-box , der später erstellt wird. Wir wissen, dass unsere Episoden Bilder haben; daher das Image-Tag.
  • Das Bild hat einen ternären Operator, der prüft, ob entweder ein episode.image oder ein episode.image.medium . Andernfalls zeigen wir eine leere Zeichenfolge an, wenn kein Bild gefunden wird. Außerdem haben wir episode.name in ein div eingefügt.

Im section zeigen wir die Staffel, zu der eine Episode gehört, und ihre Nummer. Wir haben eine Schaltfläche mit dem Text Fav . Wir haben die EpisodesList -Komponente exportiert, damit wir sie in unserer App verwenden können.

Homepage-Komponente

Wir möchten, dass die Homepage den API-Aufruf auslöst und die Episoden mithilfe der von uns erstellten EpisodesList -Komponente anzeigt. Erstellen Sie im components die HomePage -Komponente, kopieren Sie den folgenden Code und fügen Sie ihn ein:

 import React, { useContext, useEffect, lazy, Suspense } from 'react' import App from '../App' import { Store } from '../Store' import { IEpisodeProps } from '../types/interfaces' import { fetchDataAction } from '../Actions' const EpisodesList = lazy<any>(() => import('./EpisodesList')) const HomePage = (): JSX.Element => { const { state, dispatch } = useContext(Store) useEffect(() => { state.episodes.length === 0 && fetchDataAction(dispatch) }) const props: IEpisodeProps = { episodes: state.episodes, store: { state, dispatch } } return ( <App> <Suspense fallback={<div>loading...</div>}> <section className='episode-layout'> <EpisodesList {...props} /> </section> </Suspense> </App> ) } export default HomePage
  • Wir importieren useContext , useEffect , lazy und Suspense von React. Die importierte App-Komponente ist das Fundament, auf dem alle anderen Komponenten den Wert des Stores erhalten müssen.
  • Wir importieren auch Store , IEpisodeProps und FetchDataAction aus ihren jeweiligen Dateien.
  • Wir importieren die EpisodesList -Komponente mit der React.lazy Funktion, die in React 16.6 verfügbar ist.

React Lazy Loading unterstützt die Code-Splitting-Konvention. Daher wird unsere EpisodesList Komponente dynamisch geladen, anstatt sofort geladen zu werden, wodurch die Leistung unserer App verbessert wird.

  • Wir destrukturieren den state und dispatch als Requisiten aus dem Store .
  • Das kaufmännische Und (&&) im useEffect Hook prüft, ob unser Episodenstatus empty (oder gleich 0) ist. Andernfalls geben wir die Funktion fetchDataAction zurück.
  • Zuletzt geben wir die App Komponente zurück. Darin verwenden wir den Suspense -Wrapper und setzen den fallback auf ein div mit dem loading . Dies wird dem Benutzer angezeigt, während wir auf die Antwort von der API warten.
  • Die EpisodesList -Komponente wird bereitgestellt, wenn die Daten verfügbar sind, und die Daten, die die episodes enthalten, sind das, was wir darin verteilen.

Richten Sie Index.txs ein

Die Homepage -Komponente muss dem StoreProvider . Wir müssen das in der index tun. Benennen Sie index.js in index.tsx um und fügen Sie den folgenden Code ein:

 import React from 'react' import ReactDOM from 'react-dom' import './index.css' import { StoreProvider } from './Store' import HomePage from './components/HomePage' ReactDOM.render( <StoreProvider> <HomePage /> </StoreProvider>, document.getElementById('root') )

Wir importieren StoreProvider , HomePage und index.css aus ihren jeweiligen Dateien. We wrap the HomePage component in our StoreProvider . This makes it possible for the Homepage component to access the store, as we saw in the previous section.

Wir sind von weit hergekommen. Let's check what the app looks like, without any CSS.

App without CSS (Large preview)

Create Index.css

Delete the code in the index.css file and replace it with this:

 html { font-size: 14px; } body { margin: 0; padding: 0; font-size: 10px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .episode-layout { display: flex; flex-wrap: wrap; min-width: 100vh; } .episode-box { padding: .5rem; } .header { display: flex; justify-content: space-between; background: white; border-bottom: 1px solid black; padding: .5rem; position: sticky; top: 0; }

Our app now has a look and feel. Here's how it looks with CSS.

(Große Vorschau)

Now we see that our episodes can finally be fetched and displayed, because we've adopted TypeScript all the way. Great, isn't it?

Add Favorite Episodes Feature

Let's add functionality that adds favorite episodes and that links it to a separate page. Let's go back to our Store component and add a few lines of code:

Note that the highlighted code is newly added:

 import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext<IState | any>(initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload }
 case 'ADD_FAV': return { ...state, favourites: [...state.favourites, action.payload] }
 default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return <Store.Provider value={{ state, dispatch }}>{children}</Store.Provider> }

To implement the “Add favorite” feature to our app, the ADD_FAV case is added. It returns an object that holds a copy of our previous state, as well as an array with a copy of the favorite state , with the payload .

We need an action that will be called each time a user clicks on the FAV button. Let's add the highlighted code to index.tx :

 import { IAction, IEpisode, Dispatch } from './types/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => { const URL = 'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes' const data = await fetch(URL) const dataJSON = await data.json() return dispatch({ type: 'FETCH_DATA', payload: dataJSON._embedded.episodes }) }
export const toggleFavAction = (dispatch: any, episode: IEpisode | any): IAction => { let dispatchObj = { type: 'ADD_FAV', payload: episode } return dispatch(dispatchObj) }
export const toggleFavAction = (dispatch: any, episode: IEpisode | any): IAction => { let dispatchObj = { type: 'ADD_FAV', payload: episode } return dispatch(dispatchObj) }

We create a toggleFavAction function that takes dispatch and episodes as parameters, and any and IEpisode|any as their respective types, with IAction as our function type. We have an object whose type is ADD_FAV and that has episode as its payload. Lastly, we just return and dispatch the object.

Wir werden EpisodeList.tsx weitere Schnipsel hinzufügen. Kopieren Sie den markierten Code und fügen Sie ihn ein:

 import React from 'react' import { IEpisode, IProps } from '../types/interfaces' const EpisodesList = (props: IProps): Array<JSX.Element> => {
 const { episodes, toggleFavAction, favourites, store } = props const { state, dispatch } = store

 return episodes.map((episode: IEpisode) => { return ( <section key={episode.id} className='episode-box'> <img src={!!episode.image ? episode.image.medium : ''} alt={`Money Heist - ${episode.name}`} /> <div>{episode.name}</div> <section style={{ display: 'flex', justifyContent: 'space-between' }}> <div> Seasion: {episode.season} Number: {episode.number} </div> <button type='button'
 onClick={() => toggleFavAction(state, dispatch, episode)} > {favourites.find((fav: IEpisode) => fav.id === episode.id) ? 'Unfav' : 'Fav'}
 </button> </section> </section> ) }) } export default EpisodesList

Wir togglefavaction , favorites und store als Requisiten ein und destrukturieren state , eine dispatch aus dem Store. Um unsere Lieblingsepisode auszuwählen, binden wir die toggleFavAction Methode in ein onClick -Ereignis ein und übergeben die state , dispatch und episode als Argumente an die Funktion.

Zuletzt durchlaufen wir den favorite , um zu prüfen, ob fav.id (Favoriten-ID) mit episode.id . Wenn dies der Fall ist, schalten wir zwischen dem Unfav und dem Fav -Text um. Dies hilft dem Benutzer zu wissen, ob er diese Episode favorisiert hat oder nicht.

Wir nähern uns dem Ende. Aber wir brauchen immer noch eine Seite, auf der Lieblingsepisoden verlinkt werden können, wenn der Benutzer eine der Episoden auf der Startseite auswählt.

Wenn Sie so weit gekommen sind, klopfen Sie sich auf die Schulter.

Favpage-Komponente

Erstellen Sie im components eine FavPage.tsx -Datei. Kopieren Sie den folgenden Code und fügen Sie ihn ein:

 import React, { lazy, Suspense } from 'react' import App from '../App' import { Store } from '../Store' import { IEpisodeProps } from '../types/interfaces' import { toggleFavAction } from '../Actions' const EpisodesList = lazy<any>(() => import('./EpisodesList')) export default function FavPage(): JSX.Element { const { state, dispatch } = React.useContext(Store) const props: IEpisodeProps = { episodes: state.favourites, store: { state, dispatch }, toggleFavAction, favourites: state.favourites } return ( <App> <Suspense fallback={<div>loading...</div>}> <div className='episode-layout'> <EpisodesList {...props} /> </div> </Suspense> </App> ) }

Um die Logik hinter der Auswahl von Lieblingsepisoden zu erstellen, haben wir einen kleinen Code geschrieben. Wir importieren lazy und Suspense von React. Wir importieren auch Store , IEpisodeProps und toggleFavAction aus ihren jeweiligen Dateien.

Wir importieren unsere EpisodesList -Komponente mit der React.lazy Funktion. Zuletzt geben wir die App Komponente zurück. Darin verwenden wir den Suspense -Wrapper und setzen einen Fallback auf ein div mit dem Ladetext.

Dies funktioniert ähnlich wie die Homepage Komponente. Diese Komponente greift auf den Store zu, um die vom Benutzer favorisierten Episoden zu erhalten. Anschließend wird die Liste der Episoden an die EpisodesList Komponente übergeben.

Fügen wir der Datei HomePage.tsx ein paar weitere Ausschnitte hinzu.

Fügen Sie die toggleFavAction aus ../Actions . Fügen Sie auch die toggleFavAction Methode als Requisiten hinzu.

 import React, { useContext, useEffect, lazy, Suspense } from 'react' import App from '../App' import { Store } from '../Store' import { IEpisodeProps } from '../types/interfaces'
import { fetchDataAction, toggleFavAction } from '../Actions'
const EpisodesList = lazy<any>(() => import('./EpisodesList')) const HomePage = (): JSX.Element => { const { state, dispatch } = useContext(Store) useEffect(() => { state.episodes.length === 0 && fetchDataAction(dispatch) }) const props: IEpisodeProps = { episodes: state.episodes, store: { state, dispatch },
 toggleFavAction, favourites: state.favourites
 } return ( <App> <Suspense fallback={<div>loading...</div>}> <section className='episode-layout'> <EpisodesList {...props} /> </section> </Suspense> </App> ) } export default HomePage

Unsere FavPage muss verlinkt werden, also brauchen wir einen Link in unserem Header in App.tsx . Um dies zu erreichen, verwenden wir Reach Router, eine Bibliothek ähnlich wie React Router. William Le erklärt die Unterschiede zwischen Reach Router und React Router.

Führen Sie in Ihrer CLI npm install @reach/router @types/reach__router . Wir installieren sowohl die Reach-Router-Bibliothek als auch Reach reach-router Typen.

Importieren Sie nach erfolgreicher Installation den Link von @reach/router .

 import React, { useContext, Fragment } from 'react' import { Store } from './tsx'
import { Link } from '@reach/router'
 const App = ({ children }: { children: JSX.Element }): JSX.Element => {
 const { state } = useContext(Store)
return ( <Fragment> <header className='header'> <div> <h1>Money Heist</h1> <p>Pick your favourite episode</p> </div>
 <div> <Link to='/'>Home</Link> <Link to='/faves'>Favourite(s): {state.favourites.length}</Link> </div>
 </header> {children} </Fragment> ) } export default App

Wir destrukturieren den Speicher von useContext . Schließlich hat unser Zuhause einen Link und einen Pfad zu / , während unser Favorit einen Pfad zu /faves hat.

{state.favourites.length} prüft die Anzahl der Episoden in den Favoritenstaaten und zeigt sie an.

Schließlich importieren wir in unsere index.tsx -Datei die FavPage bzw. HomePage -Komponenten und packen sie in den Router ein.

Kopieren Sie den markierten Code in den vorhandenen Code:

 import React from 'react' import ReactDOM from 'react-dom' import './index.css' import { StoreProvider } from './Store'
import { Router, RouteComponentProps } from '@reach/router' import HomePage from './components/HomePage' import FavPage from './components/FavPage' const RouterPage = ( props: { pageComponent: JSX.Element } & RouteComponentProps ) => props.pageComponent
ReactDOM.render( <StoreProvider>
 <Router> <RouterPage pageComponent={<HomePage />} path='/' /> <RouterPage pageComponent={<FavPage />} path='/faves' /> </Router>
 </StoreProvider>, document.getElementById('root') )

Sehen wir uns nun an, wie das implementierte ADD_FAV funktioniert.

Der Code „Favorit hinzufügen“ funktioniert (Große Vorschau)

Favoritenfunktion entfernen

Schließlich fügen wir die Funktion „Episode entfernen“ hinzu, sodass wir beim Klicken auf die Schaltfläche zwischen dem Hinzufügen oder Entfernen einer Lieblingsepisode umschalten. Wir zeigen die Anzahl der hinzugefügten oder entfernten Episoden in der Kopfzeile an.

LADEN

Um die Funktion „Lieblingsfolge entfernen“ zu erstellen, werden wir einen weiteren Fall in unserem Shop hinzufügen. Gehen Sie also zu Store.tsx und fügen Sie den hervorgehobenen Code hinzu:

 import React, { useReducer, createContext } from 'react' import { IState, IAction } from './types/interfaces' const initialState: IState = { episodes: [], favourites: [] } export const Store = createContext<IState | any>(initialState) const reducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'FETCH_DATA': return { ...state, episodes: action.payload } case 'ADD_FAV': return { ...state, favourites: [...state.favourites, action.payload] }
 case 'REMOVE_FAV': return { ...state, favourites: action.payload }
 default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return {children} } default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return {children} } default: return state } } export const StoreProvider = ({ children }: JSX.ElementChildrenAttribute): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState) return {children} }

Wir fügen noch einen weiteren Fall namens REMOVE_FAV und geben ein Objekt zurück, das die Kopie unseres initialState . Außerdem enthält der favorites die Aktionsnutzlast.

AKTION

Kopieren Sie den folgenden hervorgehobenen Code und fügen Sie ihn in action.ts :

 import { IAction, IEpisode, IState, Dispatch } from './types/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => { const URL = 'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes' const data = await fetch(URL) const dataJSON = await data.json() return dispatch({ type: 'FETCH_DATA', payload: dataJSON.\_embedded.episodes }) } //Add IState withits type
export const toggleFavAction = (state: IState, dispatch: any, episode: IEpisode | any): IAction => { const episodeInFav = state.favourites.includes(episode)
 let dispatchObj = { type: 'ADD_FAV', payload: episode }
 if (episodeInFav) { const favWithoutEpisode = state.favourites.filter( (fav: IEpisode) => fav.id !== episode.id ) dispatchObj = { type: 'REMOVE_FAV', payload: favWithoutEpisode }
 } return dispatch(dispatchObj) }

Wir importieren die IState Schnittstelle aus ./types/interfaces , da wir sie als Typ an die state in der Funktion toggleFavAction müssen.

Eine episodeInFav Variable wird erstellt, um zu prüfen, ob es eine Folge gibt, die im favorites vorhanden ist.

Wir filtern den Status der Favoriten, um zu prüfen, ob eine Favoriten-ID nicht gleich einer Episoden-ID ist. Somit wird dem dispatchObj ein Typ von REMOVE_FAV und eine Nutzlast von favWithoutEpisode neu zugewiesen.

Sehen wir uns das Ergebnis unserer App in der Vorschau an.

Fazit

In diesem Artikel haben wir gesehen, wie man TypeScript in einem React-Projekt einrichtet und wie man ein Projekt von Vanilla React zu TypeScript migriert.

Wir haben auch eine App mit TypeScript und React erstellt, um zu sehen, wie TypeScript in React-Projekten verwendet wird. Ich vertraue darauf, dass Sie ein paar Dinge lernen konnten.

Bitte teilen Sie Ihr Feedback und Ihre Erfahrungen mit TypeScript im Kommentarbereich unten mit. Ich würde gerne sehen, was Sie sich einfallen lassen!

Das unterstützende Repository für diesen Artikel ist auf GitHub verfügbar.

Verweise

  1. „So migrieren Sie eine React-App zu TypeScript“, Joe Previte
  2. „Warum und wie Sie TypeScript in Ihrer React-App verwenden?“, Mahesh Haldar