Einrichten von TypeScript für moderne React-Projekte mit Webpack
Veröffentlicht: 2022-03-10In 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).
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.
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.
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:
- Fügen Sie
webpack.config.js
hinzu, um Webpack-bezogene Konfigurationen hinzuzufügen. - Fügen Sie
tsconfig.json
für alle unsere TypeScript-Konfigurationen hinzu. - Fügen Sie ein neues Verzeichnis hinzu,
src
. - Erstellen Sie ein neues Verzeichnis,
components
, imsrc
Ordner. - Fügen Sie schließlich
index.html
,App.tsx
undindex.tsx
imcomponents
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 vones2015
die Verwendung von ECMAScript 6-Syntax). -
module
Generiert Modulcode. -
noImplicitAny
Fehler für Deklarationen mit einem impliziertenany
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 Allgemeinenbundle.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 denawesome-typescript-loader
verwenden; - Dateien, die mit der Erweiterung
.js
, sollten mitsource-map-loader
geladen werden; - Dateien, die mit der Erweiterung
.css
enden, sollten mitcss-loader
geladen werden.
- Jede Datei, die mit der Erweiterung
-
plugins
Webpack hat seine eigenen Einschränkungen und bietet Plugins, um diese zu überwinden und seine Fähigkeiten zu erweitern. Beispielsweise erstellthtml-webpack-plugin
eine Vorlagendatei, die für den Browser aus der Dateiindex.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:
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:
- Fügen Sie TypeScript und Typen hinzu.
- Fügen Sie
tsconfig.json
. - Fangen Sie klein an.
- 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 implizitenany
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:
- Ändern Sie die Dateierweiterung in
.tsx
. - 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:
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 mitReact Lazy and Suspense
auf unsere Homepage.
- Erstellen Sie die entsprechenden Typen und Schnittstellen für unsere Episoden in
- Folgen hinzufügen.
- Store einrichten, um Episoden in
store.tsx
. - Erstellen Sie die Aktion zum Hinzufügen von Episoden in
action.ts
.
- Store einrichten, um Episoden in
- 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
.
- Richten Sie den Store zum Löschen von Episoden in
- Lieblingsfolge.
- Importieren Sie die
EpisodesList
Komponente in die Lieblingsepisode. - Render
EpisodesList
innerhalb der Lieblingsepisode.
- Importieren Sie die
- 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
.
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 diecreateContext
API von React, weshalb wir sie importiert haben. - Wir
IState
undIAction
aus./types/interfaces
importiert. - Wir haben ein
initialState
-Objekt mit dem TypIState
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 MethodecreateContext
und an dieinitialState
.
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 undstate
undaction
als Parameter übergeben. Diereducer
-Funktion hat eine switch-Anweisung, die den Wert vonaction.type
. Wenn der WertFETCH_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 derStorePrivder
Funktion haben wir denuseReducer
Hook deklariert. - Wir haben
state
unddispatch
destrukturiert. - Um unseren Shop für alle Komponenten zugänglich zu machen, haben wir einen Objektwert mit
state
unddispatch
ü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
undStoreProvider
, 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
nimmtdispatch
-Requisiten als Parameter. - Da unsere Funktion asynchron ist, würden wir
async
undawait
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 vondata.json()
haben. - Zuletzt geben wir eine Dispatch-Funktion zurück, die eine Eigenschaft vom
type
und eine Zeichenfolge vonFETCH_DATA
. Es hat auch einepayload()
._embedded.episodes
ist das Array des Episodenobjekts von unseremendpoint
.
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
undIProps
ausinterfaces.tsx
. - Als Nächstes erstellen wir eine
EpisodesList
-Funktion, die Requisiten verwendet. Die Requisiten haben den TypIProps
, während die Funktion den TypArray<JSX.Element>
hat.
Visual Studio Code schlägt vor, dass unser Funktionstyp als JSX.Element[]
geschrieben wird.
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
vonprops
, dieIEpisode
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 einenclassName
vonepisode-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 einepisode.image.medium
. Andernfalls zeigen wir eine leere Zeichenfolge an, wenn kein Bild gefunden wird. Außerdem haben wirepisode.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
undSuspense
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
undFetchDataAction
aus ihren jeweiligen Dateien. - Wir importieren die
EpisodesList
-Komponente mit derReact.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
unddispatch
als Requisiten aus demStore
. - Das kaufmännische Und (&&) im
useEffect
Hook prüft, ob unser Episodenstatusempty
(oder gleich 0) ist. Andernfalls geben wir die FunktionfetchDataAction
zurück. - Zuletzt geben wir die
App
Komponente zurück. Darin verwenden wir denSuspense
-Wrapper und setzen denfallback
auf ein div mit demloading
. 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 dieepisodes
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.
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.
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.
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
- „So migrieren Sie eine React-App zu TypeScript“, Joe Previte
- „Warum und wie Sie TypeScript in Ihrer React-App verwenden?“, Mahesh Haldar