Setarea TypeScript pentru proiecte moderne React folosind Webpack

Publicat: 2022-03-10
Rezumat rapid ↬ Acest articol prezintă Typescript, un superscript al JavaScript care prezintă caracteristica de tip static pentru identificarea erorilor obișnuite ca coduri de dezvoltator, ceea ce îmbunătățește performanța și, prin urmare, are ca rezultat aplicații de întreprindere robuste. Veți învăța, de asemenea, cum să configurați eficient TypeScript într-un proiect React, în timp ce construim o aplicație Money Heist Episode Picker, explorând TypeScript, cârlige React, cum ar fi useReducer, useContext și Reach Router.

În această eră a dezvoltării software, JavaScript poate fi folosit pentru a dezvolta aproape orice tip de aplicație. Cu toate acestea, faptul că JavaScript este tastat dinamic ar putea fi o preocupare pentru majoritatea companiilor mari, din cauza caracteristicii sale de verificare a tipului.

Din fericire, nu trebuie să așteptăm până când Comitetul Tehnic Ecma 39 introduce un sistem de tip static în JavaScript. În schimb, putem folosi TypeScript.

JavaScript, fiind tastat dinamic, nu este conștient de tipul de date al unei variabile până când acea variabilă este instanțiată în timpul execuției. Dezvoltatorii care scriu programe software mari ar putea avea tendința de a realoca o variabilă, declarată mai devreme, la o valoare de alt tip, fără niciun avertisment sau problemă, ceea ce duce la erori adesea trecute cu vederea.

În acest tutorial, vom afla ce este TypeScript și cum să lucrăm cu el într-un proiect React. Până la sfârșit, vom fi construit un proiect constând dintr-o aplicație de selectare a episoadelor pentru emisiunea TV Money Heist , folosind TypeScript și cârlige actuale asemănătoare React ( useState , useEffect , useReducer , useContext ). Cu aceste cunoștințe, puteți continua să experimentați cu TypeScript în propriile proiecte.

Acest articol nu este o introducere în TypeScript. Prin urmare, nu vom trece prin sintaxa de bază a TypeScript și JavaScript. Cu toate acestea, nu trebuie să fii un expert în niciuna dintre aceste limbi pentru a urma, pentru că vom încerca să respectăm principiul KISS (păstrați-l simplu, prost).

Mai multe după săritură! Continuați să citiți mai jos ↓

Ce este TypeScript?

În 2019, TypeScript a fost clasat pe locul șaptea cel mai folosit limbaj și pe locul cinci cu cea mai rapidă creștere pe GitHub. Dar ce este exact TypeScript?

Conform documentației oficiale, TypeScript este un superset tip de JavaScript care se compilează în JavaScript simplu. Este dezvoltat și întreținut de Microsoft și de comunitatea open-source.

„Superset” în acest context înseamnă că limbajul conține toate caracteristicile și funcționalitățile JavaScript și apoi unele. TypeScript este un limbaj de scripting tastat.

Oferă dezvoltatorilor mai mult control asupra bazei lor de cod prin adnotarea tipului, claselor și interfeței, scutind dezvoltatorii de a fi nevoiți să remedieze manual erorile enervante în consolă.

TypeScript nu a fost creat pentru a modifica JavaScript. În schimb, se extinde pe JavaScript cu funcții noi valoroase. Orice program scris în JavaScript simplu va rula, de asemenea, așa cum era de așteptat în TypeScript, inclusiv aplicațiile mobile multiplatforme și back-end-urile în Node.js.

Aceasta înseamnă că puteți scrie și aplicații React în TypeScript, așa cum vom face în acest tutorial.

De ce TypeScript?

Poate că nu ești convins să îmbrățișezi bunătatea TypeScript. Să luăm în considerare câteva dintre avantajele sale.

Mai puține bug-uri

Nu putem elimina toate erorile din codul nostru, dar le putem reduce. TypeScript verifică tipurile în timpul compilării și aruncă erori dacă tipul variabilei se modifică.

Posibilitatea de a găsi aceste erori evidente, dar frecvente atât de devreme, face mult mai ușor să vă gestionați codul cu tipuri.

Refactorizarea este mai ușoară

Probabil că deseori doriți să refactorizați destul de multe lucruri, dar pentru că ating atât de mult alt cod și multe alte fișiere, vă temeți să le modificați.

În TypeScript, astfel de lucruri pot fi adesea refactorizate cu doar un clic pe comanda „Redenumiți simbolul” din mediul dumneavoastră de dezvoltare integrat (IDE).

Redenumirea aplicației în expApp (previzualizare mare)

Într-un limbaj tatat dinamic, cum ar fi JavaScript, singura modalitate de a refactoriza mai multe fișiere în același timp este cu funcția tradițională „căutare și înlocuire” folosind expresii regulate (RegExp).

Într-un limbaj tip static, cum ar fi TypeScript, „căutați și înlocuiți” nu mai este necesar. Cu comenzile IDE precum „Găsiți toate aparițiile” și „Redenumiți simbolul”, puteți vedea toate aparițiile din aplicația funcției, clasei sau proprietății date a unei interfețe obiect.

TypeScript vă va ajuta să găsiți toate instanțele bitului refactorizat, să îl redenumiți și să vă avertizeze cu o eroare de compilare în cazul în care codul dvs. are nepotriviri de tip după refactorizare.

TypeScript are chiar mai multe avantaje decât ceea ce am acoperit aici.

Dezavantajele TypeScript

TypeScript nu este cu siguranță lipsit de dezavantaje, chiar și având în vedere caracteristicile promițătoare evidențiate mai sus.

Un fals sentiment de securitate

Caracteristica de verificare a tipului de la TypeScript creează adesea un fals sentiment de securitate în rândul dezvoltatorilor. Verificarea tipului ne avertizează într-adevăr când ceva este în neregulă cu codul nostru. Cu toate acestea, tipurile statice nu reduc densitatea generală a erorilor.

Prin urmare, puterea programului dvs. va depinde de utilizarea TypeScript, deoarece tipurile sunt scrise de dezvoltator și nu sunt verificate în timpul execuției.

Dacă căutați să utilizați TypeScript pentru a vă reduce erorile, vă rugăm să luați în considerare dezvoltarea bazată pe teste.

Sistem de tastare complicat

Sistemul de tastare, deși este un instrument excelent din multe puncte de vedere, poate fi uneori puțin complicat. Acest dezavantaj se datorează faptului că este pe deplin interoperabil cu JavaScript, ceea ce lasă și mai mult loc pentru complicații.

Cu toate acestea, TypeScript este încă JavaScript, așa că înțelegerea JavaScript este importantă.

Când să utilizați TypeScript?

Vă sfătuiesc să utilizați TypeScript în următoarele cazuri:

  • Dacă doriți să construiți o aplicație care va fi întreținută pe o perioadă lungă de timp, atunci aș recomanda insistent să începeți cu TypeScript, deoarece încurajează codul de auto-documentare, ajutând astfel alți dezvoltatori să vă înțeleagă cu ușurință codul atunci când se alătură bazei de cod. .
  • Dacă trebuie să creați o bibliotecă , luați în considerare să o scrieți în TypeScript. Acesta va ajuta editorii de cod să sugereze tipurile adecvate dezvoltatorilor care vă folosesc biblioteca.

În ultimele câteva secțiuni, am echilibrat avantajele și dezavantajele TypeScript. Să trecem la afacerea zilei: configurarea TypeScript într-un proiect modern React .

Noțiuni de bază

Există mai multe moduri de a configura TypeScript într-un proiect React. În acest tutorial, vom acoperi doar două.

Metoda 1: Creați aplicația React + TypeScript

Acum aproximativ doi ani, echipa React a lansat Create React App 2.1, cu suport TypeScript. Așadar, s-ar putea să nu trebuiască niciodată să faci eforturi grele pentru a introduce TypeScript în proiectul tău.

Anunțul TypeScript în aplicația Create React (previzualizare mare)

Pentru a începe un nou proiect Create React App, puteți rula acest...

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

… sau asta:

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

Pentru a adăuga TypeScript la un proiect Create React App, mai întâi instalați-l și @types respective:

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

… sau:

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

Apoi, redenumiți fișierele (de exemplu, index.js în index.tsx ) și reporniți serverul de dezvoltare !

A fost rapid, nu-i așa?

Metoda 2: Configurați TypeScript cu Webpack

Webpack este un pachet de module static pentru aplicații JavaScript. Preia tot codul din aplicația dvs. și îl face utilizabil într-un browser web. Modulele sunt fragmente reutilizabile de cod construite din JavaScript, node_modules , imagini și stiluri CSS ale aplicației dvs., care sunt împachetate pentru a fi utilizate cu ușurință pe site-ul dvs. web.

Creați un nou proiect

Să începem prin a crea un director nou pentru proiectul nostru:

 mkdir react-webpack cd react-webpack

Vom folosi npm pentru a inițializa proiectul nostru:

 npm init -y

Comanda de mai sus va genera un fișier package.json cu unele valori implicite. Să adăugăm și câteva dependențe pentru webpack, TypeScript și unele module specifice React.

Instalarea pachetelor

În cele din urmă, ar trebui să instalăm pachetele necesare. Deschideți interfața de linie de comandă (CLI) și rulați aceasta:

 #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

De asemenea, să adăugăm manual câteva fișiere și foldere diferite în folderul nostru react-webpack :

  1. Adăugați webpack.config.js pentru a adăuga configurații legate de webpack.
  2. Adăugați tsconfig.json pentru toate configurațiile noastre TypeScript.
  3. Adăugați un director nou, src .
  4. Creați un director nou, components , în folderul src .
  5. În cele din urmă, adăugați index.html , App.tsx și index.tsx în folderul components .

Structura proiectului

Astfel, structura noastră de foldere va arăta cam așa:

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

Începeți să adăugați un cod

Vom începe cu 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>

Acest lucru va crea HTML, cu un div gol cu ​​un ID de output .

Să adăugăm codul la componenta noastră React 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> );

Am creat un obiect de interfață și l-am numit HelloWorldProps , cu userName și lang având un tip string .

Am transmis elemente de props componentei noastre de App și am exportat-o.

Acum, să actualizăm codul în 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") );

Tocmai am importat componenta App în index.tsx . Când webpack vede orice fișier cu extensia .ts sau .tsx , acesta va transpila acel fișier folosind biblioteca awesome-typescript-loader.

Configurare TypeScript

Apoi vom adăuga o configurație la tsconfig.json :

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

Să ne uităm și la diferitele opțiuni pe care le-am adăugat la tsconfig.json :

  • compilerOptions Reprezintă diferitele opțiuni ale compilatorului.
  • jsx:react Adaugă suport pentru JSX în fișierele .tsx .
  • lib Adaugă o listă de fișiere de bibliotecă la compilație (de exemplu, utilizarea es2015 ne permite să folosim sintaxa ECMAScript 6).
  • module Generează codul modulului.
  • noImplicitAny Ridică erori pentru declarațiile cu any tip implicit.
  • outDir Reprezintă directorul de ieșire.
  • sourceMap Generează un fișier .map , care poate fi foarte util pentru depanarea aplicației.
  • target Reprezintă versiunea ECMAScript țintă la care să ne transpunem codul (putem adăuga o versiune în funcție de cerințele specifice ale browserului).
  • include Folosit pentru a specifica lista de fișiere care urmează să fie incluse.

Configurare Webpack

Să adăugăm o configurație webpack la 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", }), ], };

Să ne uităm la diferitele opțiuni pe care le-am adăugat la webpack.config.js :

  • entry Aceasta specifică punctul de intrare pentru aplicația noastră. Poate fi un singur fișier sau o serie de fișiere pe care dorim să le includem în versiunea noastră.
  • output Acesta conține configurația de ieșire. Aplicația se uită la acest lucru atunci când încearcă să scoată codul pachet din proiectul nostru pe disc. Calea reprezintă directorul de ieșire pentru codul care urmează să fie trimis, iar numele fișierului reprezintă numele fișierului pentru acesta. Este denumit în general bundle.js .
  • resolve Webpack analizează acest atribut pentru a decide dacă să grupeze sau să omite fișierul. Astfel, în proiectul nostru, webpack va lua în considerare fișierele cu extensiile .js , .jsx , .json , .ts , și .tsx pentru grupare.
  • module Putem activa webpack pentru a încărca un anumit fișier atunci când este solicitat de aplicație, folosind încărcătoare. Este nevoie de un obiect reguli care specifică că:
    • orice fișier care se termină cu extensia .tsx sau .ts ar trebui să folosească awesome-typescript-loader pentru a fi încărcat;
    • fișierele care se termină cu extensia .js ar trebui să fie încărcate cu source-map-loader ;
    • fișierele care se termină cu extensia .css ar trebui să fie încărcate cu css-loader .
  • plugins Webpack are propriile sale limitări și oferă pluginuri pentru a le depăși și a-și extinde capacitățile. De exemplu, html-webpack-plugin creează un fișier șablon care este redat browserului din fișierul index.html din directorul ./src/component/index.html .

MiniCssExtractPlugin redă fișierul CSS părinte al aplicației.

Adăugarea de scripturi la package.json

Putem adăuga diferite scripturi pentru a construi aplicații React în fișierul nostru package.json :

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

Acum, rulați npm start în CLI. Dacă totul a mers bine, ar trebui să vezi asta:

Ieșire de configurare React-Webpack (previzualizare mare)

Dacă aveți un talent pentru webpack, clonați depozitul pentru această configurare și utilizați-l în proiectele dvs.

Crearea fișierelor

Creați un folder src și un fișier index.tsx . Acesta va fi fișierul de bază care redă React.

Acum, dacă rulăm npm start , acesta va rula serverul nostru și va deschide o nouă filă. Rularea npm run build va construi webpack pentru producție și va crea un folder de compilare pentru noi.

Am văzut cum să configurați TypeScript de la zero folosind aplicația Create React și metoda de configurare a pachetului web.

Una dintre cele mai rapide moduri de a obține o înțelegere completă a TypeScript este conversia unuia dintre proiectele existente Vanilla React în TypeScript. Din păcate, adoptarea progresivă a TypeScript într-un proiect Vanilla React existent este stresantă, deoarece implică nevoia de a scoate sau de a redenumi toate fișierele, ceea ce ar duce la conflicte și o cerere de extragere uriașă dacă proiectul ar aparține unei echipe mari.

În continuare, vom analiza cum să migrați cu ușurință un proiect React la TypeScript.

Migrați o aplicație Create React existentă la TypeScript

Pentru a face acest proces mai ușor de gestionat, îl vom împărți în pași, care ne vor permite să migrăm în bucăți individuale. Iată pașii pe care îi vom face pentru a migra proiectul nostru:

  1. Adăugați TypeScript și tipuri.
  2. Adăugați tsconfig.json .
  3. Începe mic.
  4. Redenumiți extensia fișierelor în .tsx .

1. Adăugați TypeScript la proiect

Mai întâi, va trebui să adăugăm TypeScript la proiectul nostru. Presupunând că proiectul dvs. React a fost bootstrap cu aplicația Create React, putem rula următoarele:

 # 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

Observați că încă nu am schimbat nimic în TypeScript. Dacă rulăm comanda de pornire a proiectului local ( npm start sau yarn start ), nimic nu se schimbă. Dacă este cazul, atunci grozav! Suntem pregătiți pentru următorul pas.

2. Adăugați fișierul tsconfig.json

Înainte de a profita de TypeScript, trebuie să îl configuram prin fișierul tsconfig.json . Cel mai simplu mod de a începe este să schelezi unul folosind această comandă:

 npx tsc --init

Acest lucru ne aduce câteva elemente de bază, cu mult cod comentat. Acum, înlocuiți tot codul din tsconfig.json cu acesta:

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

Configurare TypeScript

Să ne uităm și la diferitele opțiuni pe care le-am adăugat la tsconfig.json :

  • compilerOptions Reprezintă diferitele opțiuni ale compilatorului.
    • target Traduce constructele JavaScript mai noi într-o versiune mai veche, cum ar fi ECMAScript 5.
    • lib Adaugă o listă de fișiere de bibliotecă la compilație (de exemplu, utilizarea es2015 ne permite să folosim sintaxa ECMAScript 6).
    • jsx:react Adaugă suport pentru JSX în fișierele .tsx .
    • lib Adaugă o listă de fișiere de bibliotecă la compilație (de exemplu, utilizarea es2015 ne permite să folosim sintaxa ECMAScript 6).
    • module Generează codul modulului.
    • noImplicitAny Folosit pentru a genera erori pentru declarațiile cu any tip implicit.
    • outDir Reprezintă directorul de ieșire.
    • sourceMap Generează un fișier .map , care poate fi foarte util pentru depanarea aplicației noastre.
    • include Folosit pentru a specifica lista de fișiere care urmează să fie incluse.

Opțiunile de configurare vor varia, în funcție de cererea unui proiect. S-ar putea să fie nevoie să verificați foaia de calcul cu opțiuni TypeScript pentru a afla ce s-ar potrivi cu proiectul dvs.

Am luat doar măsurile necesare pentru a pregăti lucrurile. Următorul nostru pas este migrarea unui fișier în TypeScript.

3. Începeți cu o componentă simplă

Profită de capacitatea TypeScript de a fi adoptat treptat. Mergeți câte un fișier în ritmul dvs. Fă ceea ce are sens pentru tine și echipa ta. Nu încercați să rezolvați totul dintr-o dată.

Pentru a o converti corect, trebuie să facem două lucruri:

  1. Schimbați extensia fișierului în .tsx .
  2. Adăugați adnotarea tipului (care ar necesita cunoștințe TypeScript).

4.Redenumiți extensiile fișierelor în .tsx

Într-o bază de cod mare, ar putea părea obositor să redenumești fișierele individual.

Redenumiți mai multe fișiere pe macOS

Redenumirea mai multor fișiere poate fi o pierdere de timp. Iată cum o poți face pe un Mac. Faceți clic dreapta (sau Ctrl + clic, sau faceți clic cu două degete simultan pe trackpad dacă utilizați un MacBook) pe folderul care conține fișierele pe care doriți să le redenumiți. Apoi, faceți clic pe „Afișați în Finder”. În Finder, selectați toate fișierele pe care doriți să le redenumiți. Faceți clic dreapta pe fișierele selectate și alegeți „Redenumiți X elemente...” Apoi, veți vedea ceva de genul acesta:

Redenumiți fișierele pe un Mac (previzualizare mare)

Introduceți șirul pe care doriți să îl găsiți și șirul cu care doriți să înlocuiți acel șir găsit și apăsați „Redenumiți”. Terminat.

Redenumiți mai multe fișiere pe Windows

Redenumirea mai multor fișiere pe Windows depășește scopul acestui tutorial, dar este disponibil un ghid complet. De obicei, veți primi erori după redenumirea fișierelor; trebuie doar să adăugați adnotările de tip. Puteți perfecționa acest lucru în documentație.

Am explicat cum să configurați TypeScript într-o aplicație React. Acum, să construim o aplicație de selectare a episoadelor pentru Money Heist folosind TypeScript.

Nu vom acoperi tipurile de bază de TypeScript. Este necesară parcurgerea documentației înainte de a continua în acest tutorial.

Timpul pentru a construi

Pentru ca acest proces să pară mai puțin descurajator, îl vom împărți în pași, care ne vor permite să construim aplicația în bucăți individuale. Iată toți pașii pe care îi vom face pentru a construi selectorul de episoade Money Heist :

  • Creați o aplicație Create React.
  • Preluați episoade.
    • Creați tipurile și interfețele adecvate pentru episoadele noastre în interface.ts .
    • Configurați magazinul pentru preluarea episoadelor în store.tsx .
    • Creați acțiunea pentru preluarea episoadelor în action.ts .
    • Creați o componentă EpisodeList.tsx care conține episoadele preluate.
    • Importați componenta EpisodesList pe pagina noastră de pornire folosind React Lazy and Suspense .
  • Adăugați episoade.
    • Configurați magazinul pentru a adăuga episoade în store.tsx .
    • Creați acțiunea pentru adăugarea de episoade în action.ts .
  • Eliminați episoade.
    • Configurați magazinul pentru ștergerea episoadelor în store.tsx .
    • Creați acțiunea pentru ștergerea episoadelor în action.ts .
  • Episodul preferat.
    • Importați componenta EpisodesList în episodul preferat.
    • Redați lista de EpisodesList în cadrul episodului preferat.
  • Folosind Reach Router pentru navigare.

Configurați React

Cel mai simplu mod de a configura React este să utilizați aplicația Create React. Create React App este o modalitate acceptată oficial de a crea aplicații React cu o singură pagină. Oferă o configurație modernă, fără configurație.

O vom folosi pentru a porni aplicația pe care o vom construi. Din CLI, rulați comanda de mai jos:

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

Odată ce instalarea a reușit, porniți serverul React rulând npm start .

Pagina de pornire React (Previzualizare mare)

Înțelegerea interfețelor și a tipurilor în dactilografia

Interfețele din TypeScript sunt folosite atunci când trebuie să dăm tipuri proprietăților obiectelor. Prin urmare, am folosi interfețe pentru a ne defini tipurile.

 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 }

La compilarea codului de mai sus, vom vedea această eroare: „Tipurile de salary de proprietate sunt incompatibile. string de tip nu poate fi atribuit number de tip .”

Astfel de erori apar în TypeScript atunci când unei proprietăți sau variabile i se atribuie un alt tip decât tipul definit. Mai exact, fragmentul de mai sus înseamnă că proprietății salary i s-a atribuit un tip de string în loc de un tip de number .

Să creăm un fișier interface.ts în folderul nostru src . Copiați și inserați acest cod în el:

 /** |-------------------------------------------------- | 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> }

Este o practică bună să adăugați un „I” la numele interfeței. Face codul lizibil. Cu toate acestea, puteți decide să-l excludeți.

Interfața IEpisode

API-ul nostru returnează un set de proprietăți, cum ar fi data de airdate , airstamp , timp de airtime , id , image , name , number , durata de runtime , season , summary și url . Prin urmare, am definit o interfață IEpisode și am setat tipurile de date adecvate proprietăților obiectului.

IState Interfață

Interfața noastră IState are episodes și, respectiv, proprietăți favorites și o interfață Array<IEpisode> .

Iacţiune

Proprietățile interfeței IAction sunt payload și type . Proprietatea type are un tip șir, în timp ce sarcina utilă are un tip de Array | any Array | any .

Rețineți că Array | any Array | any înseamnă o matrice a interfeței episodului sau orice tip.

Tipul de Dispatch este setat la React.Dispatch și o interfață <IAction> . Rețineți că React.Dispatch este tipul standard pentru funcția de dispatch , conform bazei de cod @types/react , în timp ce <IAction> este o matrice a acțiunii Interfață.

De asemenea, Visual Studio Code are un verificator TypeScript. Deci, prin simpla evidențiere sau trecerea cursorului peste cod, este suficient de inteligent pentru a sugera tipul potrivit.

Cu alte cuvinte, pentru ca noi să folosim interfața noastră în aplicațiile noastre, trebuie să o exportăm. Până acum, avem magazinul nostru și interfețele noastre care dețin tipul obiectului nostru. Acum să ne creăm magazinul. Rețineți că celelalte interfețe urmează aceleași convenții ca și cele explicate.

Preluați episoade

Crearea unui magazin

Pentru a ne prelua episoadele, avem nevoie de un magazin care să dețină starea inițială a datelor și care să definească funcția noastră de reducere.

Vom folosi cârligul useReducer pentru a configura asta. Creați un fișier store.tsx în folderul dvs. src . Copiați și inserați următorul cod în el.

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

Următorii sunt pașii pe care i-am făcut pentru a crea magazinul:

  • În definirea magazinului nostru, avem nevoie de cârligul useReducer și de API-ul createContext de la React, motiv pentru care l-am importat.
  • Am importat IState și IAction din ./types/interfaces .
  • Am declarat un obiect initialState cu un tip de IState și proprietăți ale episoadelor și, respectiv, favorite, care sunt ambele setate la o matrice goală.
  • Apoi, am creat o variabilă Store care conține metoda createContext și căreia i se transmite initialState .

Tipul metodei createContext este <IState | any> <IState | any> , ceea ce înseamnă că ar putea fi un tip de <IState> sau any . Vom vedea any tip folosit des în acest articol.

  • Apoi, am declarat o funcție reducer și am trecut în state și action ca parametri. Funcția de reducer are o instrucțiune switch care verifică valoarea action.type . Dacă valoarea este FETCH_DATA , atunci returnează un obiect care are o copie a stării noastre (...state) și a stării episodului care deține sarcina noastră de acțiune.
  • În instrucțiunea switch, returnăm o stare default .

Rețineți că parametrii de state și action din funcția reductor au tipurile IState și, respectiv, IAction . De asemenea, funcția reducer are un tip de IState .

  • În cele din urmă, am declarat o funcție StoreProvider . Acest lucru va oferi tuturor componentelor din aplicația noastră acces la magazin.
  • Această funcție ia children drept recuzită, iar în cadrul funcției StorePrivder , am declarat cârligul useReducer .
  • Am destructurat state și dispatch .
  • Pentru a face magazinul nostru accesibil tuturor componentelor, am transmis o valoare a obiectului care conține state și dispatch .

state care conține episoadele și starea noastră de favorite va fi accesibilă de către alte componente, în timp ce dispatch este o funcție care schimbă starea.

  • Vom exporta Store și StoreProvider , astfel încât să poată fi utilizat în aplicația noastră.

Creați Acțiune.ts

Va trebui să facem solicitări către API pentru a prelua episoadele care vor fi afișate utilizatorului. Acest lucru se va face într-un fișier de acțiune. Creați un fișier Action.ts și apoi inserați următorul cod:

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

În primul rând, trebuie să ne importăm interfețele, astfel încât acestea să poată fi utilizate în acest fișier. Au fost parcurși următorii pași pentru a crea acțiunea:

  • Funcția fetchDataAction ia elemente de recuzită de dispatch ca parametru.
  • Deoarece funcția noastră este asincronă, am folosi async și am fi await .
  • Creăm o variabilă ( URL ) care deține punctul nostru final API.
  • Avem o altă variabilă numită data care deține răspunsul de la API.
  • Apoi, stocăm răspunsul JSON în dataJSON , după ce am primit răspunsul în format JSON apelând data.json() .
  • În sfârșit, returnăm o funcție de expediere care are o proprietate de type și un șir de FETCH_DATA . Are, de asemenea, un payload() . _embedded.episodes este matricea obiectului episoade din punctul nostru endpoint .

Rețineți că funcția fetchDataAction preia punctul nostru final, îl convertește în obiecte JSON și returnează funcția de expediere, care actualizează starea declarată mai devreme în Magazin.

Tipul de expediere exportat este setat la React.Dispatch . Rețineți că React.Dispatch este tipul standard pentru funcția de expediere conform bazei de cod @types/react , în timp ce <IAction> este o matrice a Acțiunii de interfață.

Componenta EpisodesList

Pentru a menține reutilizabilitatea aplicației noastre, vom păstra toate episoadele preluate într-un fișier separat, apoi vom importa fișierul în componenta homePage de pornire.

În folderul components , creați un fișier EpisodesList.tsx și copiați și inserați următorul cod în el:

 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
  • Importăm IEpisode și IProps de pe interfaces.tsx .
  • Apoi, creăm o funcție EpisodesList care preia recuzită. Elementele de recuzită vor avea un tip de IProps , în timp ce funcția are un tip de Array<JSX.Element> .

Visual Studio Code sugerează ca tipul nostru de funcție să fie scris ca JSX.Element[] .

Visual Studio Code sugerează un tip (previzualizare mare)

În timp ce Array<JSX.Element> este egal cu JSX.Element[] , Array<JSX.Element> se numește identitate generică. Prin urmare, modelul generic va fi folosit adesea în acest articol.

  • În interiorul funcției, destructuram episodes din props , care are ca tip IEpisode .

Citiți despre identitatea generică. Aceste cunoștințe vor fi necesare pe măsură ce procedăm.

  • Am returnat elementele de recuzită ale episodes și le-am mapat pentru a returna câteva etichete HTML.
  • Prima secțiune conține key , care este episode.id , și un className al episode-box , care va fi creat mai târziu. Știm că episoadele noastre au imagini; prin urmare, eticheta de imagine.
  • Imaginea are un operator ternar care verifică dacă există fie un episode.image sau un episode.image.medium . În caz contrar, afișăm un șir gol dacă nu este găsită nicio imagine. De asemenea, am inclus episode.name într-un div.

În section , arătăm sezonul căruia îi aparține un episod și numărul acestuia. Avem un buton cu textul Fav . Am exportat componenta EpisodesList , astfel încât să o putem folosi în aplicația noastră.

Componenta Pagina de pornire

Dorim ca pagina de start să declanșeze apelul API și să afișeze episoadele folosind componenta EpisodesList pe care am creat-o. În interiorul folderului de components , creați componenta HomePage și copiați și inserați următorul cod în ea:

 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
  • Importăm useContext , useEffect , lazy și Suspense din React. Componenta aplicației importată este baza pe care toate celelalte componente trebuie să primească valoarea magazinului.
  • De asemenea, importăm Store , IEpisodeProps și FetchDataAction din fișierele respective.
  • Importăm componenta EpisodesList folosind caracteristica React.lazy disponibilă în React 16.6.

React lazy loading acceptă convenția de împărțire a codului. Astfel, componenta noastră EpisodesList este încărcată dinamic, în loc să fie încărcată o dată, îmbunătățind astfel performanța aplicației noastre.

  • Destructuram state si dispatch ca recuzita din Store .
  • Ampersand (&&) din cârligul useEffect verifică dacă starea episoadelor noastre este empty (sau egală cu 0). În caz contrar, returnăm funcția fetchDataAction .
  • În cele din urmă, returnăm componenta App . În interiorul acestuia, folosim învelișul Suspense și setăm fallback la un div cu textul de loading . Acesta va fi afișat utilizatorului în timp ce așteptăm răspunsul de la API.
  • Componenta EpisodesList se va monta atunci când datele sunt disponibile, iar datele care vor conține episodes sunt cele pe care le distribuim în ea.

Configurați Index.txs

Componenta Homepage trebuie să fie un copil al StoreProvider . Va trebui să facem asta în fișierul index . Redenumiți index.js în index.tsx și inserați următorul cod:

 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') )

Importăm StoreProvider , HomePage și index.css din fișierele respective. 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.

Am parcurs un drum lung. 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.

(Previzualizare mare)

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.

Vom adăuga mai multe fragmente pe EpisodeList.tsx . Copiați și inserați codul evidențiat:

 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

togglefavaction , favorites și store ca elemente de recuzită și destructuram state , o dispatch din magazin. Pentru a selecta episodul nostru preferat, includem metoda toggleFavAction într-un eveniment onClick și transmitem elementele de recuzită pentru state , dispatch și episode ca argumente pentru funcție.

În cele din urmă, parcurgem starea favorite pentru a verifica dacă fav.id (ID favorit) se potrivește cu episode.id . Dacă se întâmplă, comutăm între textul Unfav și Fav . Acest lucru îl ajută pe utilizator să știe dacă a preferat acel episod sau nu.

Ne apropiem de final. Dar mai avem nevoie de o pagină la care episoadele preferate să poată fi conectate atunci când utilizatorul alege dintre episoadele de pe pagina de pornire.

Dacă ai ajuns atât de departe, dă-ți o palmă pe spate.

Componentă Favpage

În folderul components , creați un fișier FavPage.tsx . Copiați și inserați următorul cod în el:

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

Pentru a crea logica din spatele alegerii episoadelor preferate, am scris un mic cod. Importăm lazy și Suspense din React. De asemenea, importăm Store , IEpisodeProps și toggleFavAction din fișierele respective.

Importăm componenta EpisodesList folosind funcția React.lazy . În cele din urmă, returnăm componenta App . În interiorul acestuia, folosim învelișul Suspense și setăm o alternativă la un div cu textul de încărcare.

Aceasta funcționează similar cu componenta Homepage de pornire. Această componentă va accesa magazinul pentru a obține episoadele preferate de utilizator. Apoi, lista de episoade este trecută la componenta EpisodesList .

Să mai adăugăm câteva fragmente în fișierul HomePage.tsx .

Includeți toggleFavAction din ../Actions . Includeți și metoda toggleFavAction ca elemente de recuzită.

 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

Pagina noastră FavPage trebuie să fie conectată, așa că avem nevoie de un link în antetul nostru în App.tsx . Pentru a realiza acest lucru, folosim Reach Router, o bibliotecă similară cu React Router. William Le explică diferențele dintre Reach Router și React Router.

În CLI, rulați npm install @reach/router @types/reach__router . Instalăm atât biblioteca Reach Router, cât și tipurile reach-router .

După instalarea cu succes, importați Link din @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

Destructuram magazinul din useContext . În cele din urmă, casa noastră va avea un Link și o cale către / , în timp ce preferatul nostru are o cale către /faves .

{state.favourites.length} verifică numărul de episoade din stările favorite și îl afișează.

În cele din urmă, în fișierul nostru index.tsx , importăm componentele FavPage și, respectiv, HomePage și le împachetăm în Router .

Copiați codul evidențiat în codul existent:

 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') )

Acum, să vedem cum funcționează ADD_FAV implementat.

Codul „Adăugați favorit” funcționează (previzualizare mare)

Eliminați funcționalitatea preferată

În cele din urmă, vom adăuga funcția „Eliminare episod”, astfel încât atunci când se face clic pe butonul, să comutăm între adăugarea sau eliminarea unui episod preferat. Vom afișa în antet numărul de episoade adăugate sau eliminate.

MAGAZIN

Pentru a crea funcționalitatea „Eliminați episodul favorit”, vom adăuga un alt caz în magazinul nostru. Deci, accesați Store.tsx și adăugați codul evidențiat:

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

Adăugăm încă un caz numit REMOVE_FAV și returnăm un obiect care conține copia initialState . De asemenea, starea favorites conține încărcătura utilă de acțiune.

ACȚIUNE

Copiați următorul cod evidențiat și inserați-l în 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) }

Importăm interfața IState din ./types/interfaces , pentru că va trebui să o transmitem ca tip la elementele de recuzită de state din funcția toggleFavAction .

O variabilă episodeInFav este creată pentru a verifica dacă există un episod care există în starea favorites .

Filtrăm prin starea favorite pentru a verifica dacă un ID favorit nu este egal cu un ID de episod. Astfel, dispatchObj este reatribuit un tip de REMOVE_FAV și o sarcină utilă de favWithoutEpisode .

Să previzualizam rezultatul aplicației noastre.

Concluzie

În acest articol, am văzut cum să configurați TypeScript într-un proiect React și cum să migrați un proiect de la vanilla React la TypeScript.

De asemenea, am creat o aplicație cu TypeScript și React pentru a vedea cum este folosit TypeScript în proiectele React. Am încredere că ai reușit să înveți câteva lucruri.

Vă rugăm să împărtășiți feedback-ul și experiențele dvs. cu TypeScript în secțiunea de comentarii de mai jos. Mi-ar plăcea să văd cu ce ai venit!

Depozitul de suport pentru acest articol este disponibil pe GitHub.

Referințe

  1. „Cum să migrezi o aplicație React la TypeScript”, Joe Previte
  2. „De ce și cum să utilizați TypeScript în aplicația dvs. React?”, Mahesh Haldar