Setarea TypeScript pentru proiecte moderne React folosind Webpack
Publicat: 2022-03-10Î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).
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).
Î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.
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
:
- Adăugați
webpack.config.js
pentru a adăuga configurații legate de webpack. - Adăugați
tsconfig.json
pentru toate configurațiile noastre TypeScript. - Adăugați un director nou,
src
. - Creați un director nou,
components
, în folderulsrc
. - În cele din urmă, adăugați
index.html
,App.tsx
șiindex.tsx
în folderulcomponents
.
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, utilizareaes2015
ne permite să folosim sintaxa ECMAScript 6). -
module
Generează codul modulului. -
noImplicitAny
Ridică erori pentru declarațiile cuany
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 generalbundle.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 cusource-map-loader
; - fișierele care se termină cu extensia
.css
ar trebui să fie încărcate cucss-loader
.
- orice fișier care se termină cu extensia
-
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șierulindex.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:
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:
- Adăugați TypeScript și tipuri.
- Adăugați
tsconfig.json
. - Începe mic.
- 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 cuany
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:
- Schimbați extensia fișierului în
.tsx
. - 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:
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 folosindReact Lazy and Suspense
.
- Creați tipurile și interfețele adecvate pentru episoadele noastre în
- 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
.
- Configurați magazinul pentru a adăuga episoade în
- Eliminați episoade.
- Configurați magazinul pentru ștergerea episoadelor în
store.tsx
. - Creați acțiunea pentru ștergerea episoadelor în
action.ts
.
- Configurați magazinul pentru ștergerea episoadelor în
- Episodul preferat.
- Importați componenta
EpisodesList
în episodul preferat. - Redați lista de
EpisodesList
în cadrul episodului preferat.
- Importați componenta
- 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
.
Î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-ulcreateContext
de la React, motiv pentru care l-am importat. - Am importat
IState
șiIAction
din./types/interfaces
. - Am declarat un obiect
initialState
cu un tip deIState
ș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 metodacreateContext
și căreia i se transmiteinitialState
.
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 înstate
șiaction
ca parametri. Funcția dereducer
are o instrucțiune switch care verifică valoareaaction.type
. Dacă valoarea esteFETCH_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țieiStorePrivder
, am declarat cârliguluseReducer
. - Am destructurat
state
șidispatch
. - Pentru a face magazinul nostru accesibil tuturor componentelor, am transmis o valoare a obiectului care conține
state
șidispatch
.
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
șiStoreProvider
, 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ă dedispatch
ca parametru. - Deoarece funcția noastră este asincronă, am folosi
async
și am fiawait
. - 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ânddata.json()
. - În sfârșit, returnăm o funcție de expediere care are o proprietate de
type
și un șir deFETCH_DATA
. Are, de asemenea, unpayload()
._embedded.episodes
este matricea obiectului episoade din punctul nostruendpoint
.
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
șiIProps
de peinterfaces.tsx
. - Apoi, creăm o funcție
EpisodesList
care preia recuzită. Elementele de recuzită vor avea un tip deIProps
, în timp ce funcția are un tip deArray<JSX.Element>
.
Visual Studio Code sugerează ca tipul nostru de funcție să fie scris ca JSX.Element[]
.
Î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
dinprops
, care are ca tipIEpisode
.
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 esteepisode.id
, și unclassName
alepisode-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 unepisode.image.medium
. În caz contrar, afișăm un șir gol dacă nu este găsită nicio imagine. De asemenea, am inclusepisode.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
șiSuspense
din React. Componenta aplicației importată este baza pe care toate celelalte componente trebuie să primească valoarea magazinului. - De asemenea, importăm
Store
,IEpisodeProps
șiFetchDataAction
din fișierele respective. - Importăm componenta
EpisodesList
folosind caracteristicaReact.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
sidispatch
ca recuzita dinStore
. - Ampersand (&&) din cârligul
useEffect
verifică dacă starea episoadelor noastre esteempty
(sau egală cu 0). În caz contrar, returnăm funcțiafetchDataAction
. - În cele din urmă, returnăm componenta
App
. În interiorul acestuia, folosim învelișulSuspense
și setămfallback
la un div cu textul deloading
. 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țineepisodes
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.
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.
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.
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
- „Cum să migrezi o aplicație React la TypeScript”, Joe Previte
- „De ce și cum să utilizați TypeScript în aplicația dvs. React?”, Mahesh Haldar