Mengatur TypeScript Untuk Proyek React Modern Menggunakan Webpack

Diterbitkan: 2022-03-10
Ringkasan cepat Artikel ini memperkenalkan TypeScript, superskrip JavaScript yang menyajikan fitur tipe statis untuk menemukan kesalahan umum sebagai kode pengembang, yang meningkatkan kinerja, sehingga menghasilkan aplikasi perusahaan yang tangguh. Anda juga akan belajar cara mengatur TypeScript secara efisien dalam Proyek React saat kami membangun Aplikasi Pemilih Episode Pencurian Uang, menjelajahi TypeScript, kait React seperti useReducer, useContext, dan Reach Router.

Di era pengembangan perangkat lunak ini, JavaScript dapat digunakan untuk mengembangkan hampir semua jenis aplikasi. Namun, fakta bahwa JavaScript diketik secara dinamis dapat menjadi perhatian bagi sebagian besar perusahaan perusahaan besar, karena fitur pengecekan tipe yang longgar.

Untungnya, kita tidak perlu menunggu sampai Ecma Technical Committee 39 memperkenalkan sistem tipe statis ke dalam JavaScript. Kita bisa menggunakan TypeScript sebagai gantinya.

JavaScript, yang diketik secara dinamis, tidak mengetahui tipe data variabel sampai variabel itu dipakai saat runtime. Pengembang yang menulis program perangkat lunak besar mungkin memiliki kecenderungan untuk menetapkan kembali variabel, yang dideklarasikan sebelumnya, ke nilai jenis yang berbeda, tanpa peringatan atau masalah apa pun, yang mengakibatkan bug sering diabaikan.

Dalam tutorial ini, kita akan mempelajari apa itu TypeScript dan bagaimana bekerja dengannya dalam proyek React. Pada akhirnya, kita akan membangun sebuah proyek yang terdiri dari aplikasi pemilih episode untuk acara TV Money Heist , menggunakan TypeScript dan kait mirip React saat ini ( useState , useEffect , useReducer , useContext ). Dengan pengetahuan ini, Anda dapat terus bereksperimen dengan TypeScript di proyek Anda sendiri.

Artikel ini bukan pengantar TypeScript. Oleh karena itu, kita tidak akan membahas sintaks dasar TypeScript dan JavaScript. Namun, Anda tidak harus menjadi ahli dalam salah satu bahasa ini untuk mengikuti, karena kami akan mencoba mengikuti prinsip KISS (tetap sederhana, bodoh).

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Apa itu TypeScript?

Pada tahun 2019, TypeScript menduduki peringkat bahasa ketujuh yang paling banyak digunakan dan bahasa dengan pertumbuhan tercepat kelima di GitHub. Tapi apa sebenarnya TypeScript itu?

Menurut dokumentasi resmi, TypeScript adalah superset JavaScript yang diketik yang dikompilasi ke JavaScript biasa. Ini dikembangkan dan dikelola oleh Microsoft dan komunitas sumber terbuka.

"Superset" dalam konteks ini berarti bahwa bahasa tersebut berisi semua fitur dan fungsionalitas JavaScript dan beberapa lainnya. TypeScript adalah bahasa script yang diketik.

Ini menawarkan pengembang lebih banyak kontrol atas basis kode mereka melalui anotasi jenis, kelas, dan antarmuka, sehingga pengembang tidak perlu memperbaiki bug yang mengganggu secara manual di konsol.

TypeScript tidak dibuat untuk mengubah JavaScript. Sebaliknya, ia memperluas JavaScript dengan fitur-fitur baru yang berharga. Program apa pun yang ditulis dalam JavaScript biasa juga akan berjalan seperti yang diharapkan di TypeScript, termasuk aplikasi seluler lintas platform dan back end di Node.js.

Ini berarti Anda juga dapat menulis aplikasi React di TypeScript, seperti yang akan kita lakukan dalam tutorial ini.

Mengapa TypeScript?

Mungkin, Anda tidak yakin untuk menerima kebaikan TypeScript. Mari kita pertimbangkan beberapa keuntungannya.

Lebih sedikit Bug

Kami tidak dapat menghilangkan semua bug dalam kode kami, tetapi kami dapat menguranginya. TypeScript memeriksa jenis pada waktu kompilasi dan melempar kesalahan jika jenis variabel berubah.

Mampu menemukan kesalahan yang jelas namun sering ini sejak awal membuatnya jauh lebih mudah untuk mengelola kode Anda dengan tipe.

Memfaktorkan Ulang Lebih Mudah

Anda mungkin sering ingin memfaktorkan ulang cukup banyak hal, tetapi karena mereka menyentuh begitu banyak kode lain dan banyak file lain, Anda berhati-hati untuk memodifikasinya.

Di TypeScript, hal-hal seperti itu seringkali dapat di-refactored hanya dengan mengklik perintah "Ganti nama simbol" di lingkungan pengembangan terintegrasi (IDE) Anda.

Mengganti nama aplikasi menjadi expApp (Pratinjau besar)

Dalam bahasa yang diketik secara dinamis seperti JavaScript, satu-satunya cara untuk memfaktorkan ulang banyak file secara bersamaan adalah dengan fungsi "cari dan ganti" tradisional menggunakan ekspresi reguler (RegExp).

Dalam bahasa yang diketik secara statis seperti TypeScript, "cari dan ganti" tidak diperlukan lagi. Dengan perintah IDE seperti "Temukan semua kemunculan" dan "Ganti nama simbol", Anda dapat melihat semua kemunculan di aplikasi dari fungsi, kelas, atau properti yang diberikan dari antarmuka objek.

TypeScript akan membantu Anda menemukan semua contoh bit refactored, mengganti namanya, dan mengingatkan Anda dengan kesalahan kompilasi jika kode Anda memiliki jenis ketidakcocokan setelah refactoring.

TypeScript bahkan memiliki lebih banyak keuntungan daripada yang telah kita bahas di sini.

Kekurangan TypeScript

TypeScript tentu bukan tanpa kekurangannya, bahkan dengan fitur-fitur menjanjikan yang disorot di atas.

Rasa Aman Palsu

Fitur pengecekan tipe TypeScript sering kali menciptakan rasa aman yang salah di antara pengembang. Pengecekan tipe memang memperingatkan kita ketika ada yang salah dengan kode kita. Namun, tipe statis tidak mengurangi kepadatan bug secara keseluruhan.

Oleh karena itu, kekuatan program Anda akan bergantung pada penggunaan TypeScript, karena tipe ditulis oleh pengembang dan tidak diperiksa saat runtime.

Jika Anda mencari TypeScript untuk mengurangi bug Anda, harap pertimbangkan pengembangan berbasis pengujian sebagai gantinya.

Sistem Pengetikan yang Rumit

Sistem pengetikan, meskipun merupakan alat yang hebat dalam banyak hal, terkadang bisa sedikit rumit. Kelemahan ini berasal dari interoperabilitas sepenuhnya dengan JavaScript, yang menyisakan lebih banyak ruang untuk komplikasi.

Namun, TypeScript masih JavaScript, jadi memahami JavaScript itu penting.

Kapan Menggunakan TypeScript?

Saya akan menyarankan Anda untuk menggunakan TypeScript dalam kasus berikut:

  • Jika Anda ingin membangun aplikasi yang akan dipelihara dalam jangka waktu yang lama , maka saya sangat menyarankan untuk memulai dengan TypeScript, karena ini mendorong kode yang mendokumentasikan diri sendiri, sehingga membantu pengembang lain untuk memahami kode Anda dengan mudah saat mereka bergabung dengan basis kode Anda. .
  • Jika Anda perlu membuat library , pertimbangkan untuk menulisnya di TypeScript. Ini akan membantu editor kode untuk menyarankan jenis yang sesuai untuk pengembang yang menggunakan perpustakaan Anda.

Dalam beberapa bagian terakhir, kami telah menyeimbangkan pro dan kontra dari TypeScript. Mari beralih ke bisnis hari ini: menyiapkan TypeScript di proyek React modern .

Mulai

Ada beberapa cara untuk mengatur TypeScript dalam Proyek Bereaksi. Dalam tutorial ini, kita hanya akan membahas dua.

Metode 1: Buat Aplikasi React + TypeScript

Sekitar dua tahun lalu, tim React merilis Create React App 2.1, dengan dukungan TypeScript. Jadi, Anda mungkin tidak perlu melakukan pekerjaan berat apa pun untuk memasukkan TypeScript ke dalam proyek Anda.

Pengumuman TypeScript di Create React App (Pratinjau besar)

Untuk memulai proyek Create React App baru, Anda dapat menjalankan ini…

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

… atau ini:

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

Untuk menambahkan TypeScript ke proyek Create React App, pertama-tama instal dan masing-masing @types :

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

… atau:

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

Selanjutnya, ganti nama file (misalnya, index.js menjadi index.tsx ), dan mulai ulang server pengembangan Anda !

Itu cepat, bukan?

Metode 2: Mengatur TypeScript Dengan Webpack

Webpack adalah bundler modul statis untuk aplikasi JavaScript. Dibutuhkan semua kode dari aplikasi Anda dan membuatnya dapat digunakan di browser web. Modul adalah potongan kode yang dapat digunakan kembali yang dibuat dari JavaScript, node_modules , gambar, dan gaya CSS aplikasi Anda, yang dikemas agar mudah digunakan di situs web Anda.

Buat Proyek Baru

Mari kita mulai dengan membuat direktori baru untuk proyek kita:

 mkdir react-webpack cd react-webpack

Kami akan menggunakan npm untuk menginisialisasi proyek kami:

 npm init -y

Perintah di atas akan menghasilkan file package.json dengan beberapa nilai default. Mari tambahkan juga beberapa dependensi untuk webpack, TypeScript, dan beberapa modul khusus React.

Menginstal Paket

Terakhir, kita perlu menginstal paket yang diperlukan. Buka antarmuka baris perintah (CLI) Anda dan jalankan ini:

 #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

Mari kita juga menambahkan beberapa file dan folder berbeda secara manual di bawah folder react-webpack kita:

  1. Tambahkan webpack.config.js untuk menambahkan konfigurasi terkait webpack.
  2. Tambahkan tsconfig.json untuk semua konfigurasi TypeScript kami.
  3. Tambahkan direktori baru, src .
  4. Buat direktori baru, components , di folder src .
  5. Terakhir, tambahkan index.html , App.tsx , dan index.tsx di folder components .

Struktur Proyek

Dengan demikian, struktur folder kita akan terlihat seperti ini:

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

Mulai Menambahkan Beberapa Kode

Kita akan mulai dengan 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>

Ini akan membuat HTML, dengan div kosong dengan ID output .

Mari tambahkan kode ke komponen React kita 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> );

Kami telah membuat objek antarmuka dan menamakannya HelloWorldProps , dengan userName dan lang memiliki tipe string .

Kami meneruskan props ke komponen App kami dan mengekspornya.

Sekarang, mari kita perbarui kode di 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") );

Kami baru saja mengimpor komponen App ke index.tsx . Ketika webpack melihat file apa pun dengan ekstensi .ts atau .tsx , itu akan mengubah file tersebut menggunakan library awesome-typescript-loader.

Konfigurasi TypeScript

Kami kemudian akan menambahkan beberapa konfigurasi ke tsconfig.json :

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

Mari kita lihat juga berbagai opsi yang kami tambahkan ke tsconfig.json :

  • compilerOptions Mewakili opsi compiler yang berbeda.
  • jsx:react Menambahkan dukungan untuk JSX dalam file .tsx .
  • lib Menambahkan daftar file perpustakaan ke kompilasi (misalnya, menggunakan es2015 memungkinkan kita untuk menggunakan sintaks ECMAScript 6).
  • module Menghasilkan kode modul.
  • noImplicitAny Menimbulkan kesalahan untuk deklarasi dengan tipe any yang tersirat.
  • outDir Mewakili direktori keluaran.
  • sourceMap Menghasilkan file .map , yang bisa sangat berguna untuk men-debug aplikasi.
  • target Mewakili versi ECMAScript target untuk mengubah kode kita menjadi (kita dapat menambahkan versi berdasarkan kebutuhan browser spesifik kita).
  • include Digunakan untuk menentukan daftar file yang akan disertakan.

Konfigurasi Paket Web

Mari tambahkan beberapa konfigurasi webpack ke 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", }), ], };

Mari kita lihat berbagai opsi yang telah kita tambahkan ke webpack.config.js :

  • entry Ini menentukan titik masuk untuk aplikasi kita. Ini mungkin file tunggal atau array file yang ingin kita sertakan dalam build.
  • output Ini berisi konfigurasi output. Aplikasi melihat ini ketika mencoba mengeluarkan kode yang dibundel dari proyek kami ke disk. Path mewakili direktori keluaran untuk kode yang akan dikeluarkan, dan nama file mewakili nama file yang sama. Biasanya bernama bundle.js .
  • resolve Webpack melihat atribut ini untuk memutuskan apakah akan menggabungkan atau melewatkan file. Jadi, dalam proyek kami, webpack akan mempertimbangkan file dengan ekstensi .js , .jsx , .json , .ts , dan .tsx untuk bundling.
  • module Kami dapat mengaktifkan webpack untuk memuat file tertentu saat diminta oleh aplikasi, menggunakan loader. Dibutuhkan objek aturan yang menentukan bahwa:
    • file apa pun yang diakhiri dengan ekstensi .tsx atau .ts harus menggunakan awesome-typescript-loader untuk dimuat;
    • file yang diakhiri dengan ekstensi .js harus dimuat dengan source-map-loader ;
    • file yang diakhiri dengan ekstensi .css harus dimuat dengan css-loader .
  • plugins Webpack memiliki keterbatasannya sendiri, dan menyediakan plugin untuk mengatasinya dan memperluas kemampuannya. Misalnya, html-webpack-plugin membuat file template yang dirender ke browser dari file index.html di direktori ./src/component/index.html .

MiniCssExtractPlugin merender file CSS induk aplikasi.

Menambahkan Script Ke package.json

Kami dapat menambahkan skrip yang berbeda untuk membangun aplikasi React di file package.json kami:

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

Sekarang, jalankan npm start di CLI Anda. Jika semuanya berjalan dengan baik, Anda akan melihat ini:

Output pengaturan React-Webpack (Pratinjau besar)

Jika Anda memiliki kemampuan untuk webpack, klon repositori untuk pengaturan ini, dan gunakan di seluruh proyek Anda.

Membuat File

Buat folder src dan file index.tsx . Ini akan menjadi file dasar yang merender React.

Sekarang, jika kita menjalankan npm start , itu akan menjalankan server kita dan membuka tab baru. Menjalankan npm run build akan membangun webpack untuk produksi dan akan membuat folder build untuk kita.

Kita telah melihat cara mengatur TypeScript dari awal menggunakan metode Create React App dan konfigurasi webpack.

Salah satu cara tercepat untuk memahami sepenuhnya TypeScript adalah dengan mengonversi salah satu proyek Vanilla React Anda ke TypeScript. Sayangnya, secara bertahap mengadopsi TypeScript dalam proyek Vanilla React yang ada membuat stres karena mengharuskan harus mengeluarkan atau mengganti nama semua file, yang akan mengakibatkan konflik dan permintaan tarik raksasa jika proyek tersebut milik tim besar.

Selanjutnya, kita akan melihat cara mudah memigrasikan proyek React ke TypeScript.

Migrasikan Aplikasi Buat Bereaksi yang Ada Ke TypeScript

Untuk membuat proses ini lebih mudah dikelola, kami akan memecahnya menjadi beberapa langkah, yang akan memungkinkan kami untuk bermigrasi dalam potongan individu. Berikut adalah langkah-langkah yang akan kami ambil untuk memigrasikan proyek kami:

  1. Tambahkan TypeScript dan jenis.
  2. Tambahkan tsconfig.json .
  3. Mulai dari yang kecil.
  4. Ganti nama ekstensi file menjadi .tsx .

1. Tambahkan TypeScript ke Proyek

Pertama, kita perlu menambahkan TypeScript ke proyek kita. Dengan asumsi bahwa proyek React Anda di-bootstrap dengan Create React App, kita dapat menjalankan yang berikut:

 # 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

Perhatikan bahwa kami belum mengubah apa pun ke TypeScript. Jika kita menjalankan perintah untuk memulai proyek secara lokal ( npm start atau yarn start ), tidak ada yang berubah. Jika itu masalahnya, maka bagus! Kami siap untuk langkah selanjutnya.

2. Tambahkan File tsconfig.json

Sebelum memanfaatkan TypeScript, kita perlu mengkonfigurasinya melalui file tsconfig.json . Cara termudah untuk memulai adalah dengan membuat perancah menggunakan perintah ini:

 npx tsc --init

Ini memberi kami beberapa dasar, dengan banyak kode yang dikomentari. Sekarang, ganti semua kode di tsconfig.json dengan ini:

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

Konfigurasi TypeScript

Mari kita lihat juga berbagai opsi yang kami tambahkan ke tsconfig.json :

  • compilerOptions Mewakili opsi compiler yang berbeda.
    • target Menerjemahkan konstruksi JavaScript yang lebih baru ke versi yang lebih lama, seperti ECMAScript 5.
    • lib Menambahkan daftar file perpustakaan ke kompilasi (misalnya, menggunakan es2015 memungkinkan kita untuk menggunakan sintaks ECMAScript 6).
    • jsx:react Menambahkan dukungan untuk JSX dalam file .tsx .
    • lib Menambahkan daftar file perpustakaan ke kompilasi (misalnya, menggunakan es2015 memungkinkan kita untuk menggunakan sintaks ECMAScript 6).
    • module Menghasilkan kode modul.
    • noImplicitAny Digunakan untuk memunculkan kesalahan untuk deklarasi dengan tipe any yang tersirat.
    • outDir Mewakili direktori keluaran.
    • sourceMap Menghasilkan file .map , yang bisa sangat berguna untuk men-debug aplikasi kita.
    • include Digunakan untuk menentukan daftar file yang akan disertakan.

Opsi konfigurasi akan bervariasi, sesuai dengan permintaan proyek. Anda mungkin perlu memeriksa spreadsheet opsi TypeScript untuk mencari tahu apa yang sesuai dengan proyek Anda.

Kami hanya mengambil tindakan yang diperlukan untuk menyiapkan segalanya. Langkah kita selanjutnya adalah memigrasikan file ke TypeScript.

3. Mulai Dengan Komponen Sederhana

Manfaatkan kemampuan TypeScript untuk diadopsi secara bertahap. Buka satu file pada satu waktu dengan kecepatan Anda sendiri. Lakukan apa yang masuk akal bagi Anda dan tim Anda. Jangan mencoba untuk menangani semuanya sekaligus.

Untuk mengonversi ini dengan benar, kita perlu melakukan dua hal:

  1. Ubah ekstensi file menjadi .tsx .
  2. Tambahkan anotasi tipe (yang akan membutuhkan pengetahuan TypeScript).

4. Ganti Nama Ekstensi File menjadi .tsx

Dalam basis kode yang besar, mungkin melelahkan untuk mengganti nama file satu per satu.

Ganti nama banyak file di macOS

Mengganti nama banyak file dapat membuang-buang waktu. Inilah cara Anda dapat melakukannya di Mac. Klik kanan (atau Ctrl + klik, atau klik dengan dua jari secara bersamaan di trackpad jika Anda menggunakan MacBook) pada folder yang berisi file yang ingin Anda ganti namanya. Kemudian, klik "Ungkapkan di Finder". Di Finder, pilih semua file yang ingin Anda ganti namanya. Klik kanan file yang dipilih, dan pilih "Ganti nama item X ..." Kemudian, Anda akan melihat sesuatu seperti ini:

Mengganti nama file di Mac (Pratinjau besar)

Masukkan string yang ingin Anda temukan, dan string yang ingin Anda gunakan untuk mengganti string yang ditemukan, dan tekan "Ganti nama". Selesai.

Ganti nama banyak file di Windows

Mengganti nama banyak file di Windows berada di luar cakupan tutorial ini, tetapi panduan lengkap tersedia. Anda biasanya akan mendapatkan kesalahan setelah mengganti nama file; Anda hanya perlu menambahkan anotasi jenis. Anda dapat memoles ini di dokumentasi.

Kami telah membahas cara mengatur TypeScript di aplikasi React. Sekarang, mari buat aplikasi pemilih episode untuk Money Heist menggunakan TypeScript.

Kami tidak akan membahas tipe dasar TypeScript. Diperlukan melalui dokumentasi sebelum melanjutkan dalam tutorial ini.

Saatnya Membangun

Untuk membuat proses ini terasa tidak terlalu menakutkan, kita akan memecahnya menjadi beberapa langkah, yang akan memungkinkan kita untuk membangun aplikasi dalam potongan individu. Berikut adalah semua langkah yang akan kami ambil untuk membuat pemetik episode Money Heist :

  • Scaffold pada Create React App.
  • Ambil episode.
    • Buat jenis dan antarmuka yang sesuai untuk episode kami di interface.ts .
    • Siapkan toko untuk mengambil episode di store.tsx .
    • Buat aksi untuk mengambil episode di action.ts .
    • Buat komponen EpisodeList.tsx yang menyimpan episode yang diambil.
    • Impor komponen EpisodesList ke halaman beranda kami menggunakan React Lazy and Suspense .
  • Tambahkan episode.
    • Siapkan toko untuk menambahkan episode di store.tsx .
    • Buat aksi untuk menambahkan episode di action.ts .
  • Hapus episode.
    • Siapkan toko untuk menghapus episode di store.tsx .
    • Buat aksi untuk menghapus episode di action.ts .
  • Adegan favorit.
    • Impor komponen EpisodesList di episode favorit.
    • Render EpisodesList di dalam episode favorit.
  • Menggunakan Reach Router untuk navigasi.

Mengatur Reaksi

Cara termudah untuk mengatur React adalah dengan menggunakan Create React App. Create React App adalah cara yang didukung secara resmi untuk membuat aplikasi React satu halaman. Ini menawarkan pengaturan build modern tanpa konfigurasi.

Kita akan menggunakannya untuk bootstrap aplikasi yang akan kita bangun. Dari CLI Anda, jalankan perintah di bawah ini:

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

Setelah instalasi berhasil, jalankan server React dengan menjalankan npm start .

Bereaksi halaman awal (Pratinjau besar)

Memahami Antarmuka Dan Jenis Dalam TypeScript

Antarmuka dalam TypeScript digunakan ketika kita perlu memberikan tipe ke properti objek. Oleh karena itu, kami akan menggunakan antarmuka untuk mendefinisikan tipe kami.

 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 }

Saat mengkompilasi kode di atas, kita akan melihat kesalahan ini: “Jenis salary properti tidak sesuai. Ketik string tidak dapat ditetapkan untuk mengetikkan number .”

Kesalahan seperti itu terjadi di TypeScript ketika properti atau variabel diberi tipe selain tipe yang ditentukan. Secara khusus, cuplikan di atas berarti bahwa properti salary diberi tipe string , bukan tipe number .

Mari buat file interface.ts di folder src kita. Salin dan tempel kode ini ke dalamnya:

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

Ini adalah praktik yang baik untuk menambahkan "I" ke nama antarmuka. Itu membuat kode dapat dibaca. Namun, Anda dapat memutuskan untuk mengecualikannya.

Antarmuka episode IE

API kami mengembalikan sekumpulan properti seperti airdate , airstamp , airtime , id , image , name , number , runtime , season , summary , dan url . Oleh karena itu, kami mendefinisikan antarmuka IEpisode dan mengatur tipe data yang sesuai ke properti objek.

Antarmuka Negara

Antarmuka IState kami masing-masing memiliki properti episodes dan favorites , dan antarmuka Array<IEpisode> .

tindakan IA

Properti antarmuka IAction adalah payload dan type . Properti type memiliki tipe string, sedangkan payload memiliki tipe Array | any Array | any

Perhatikan bahwa Array | any Array | any berarti larik antarmuka episode atau jenis apa pun.

Jenis Dispatch diatur ke React.Dispatch dan antarmuka <IAction> . Perhatikan bahwa React.Dispatch adalah tipe standar untuk fungsi dispatch , menurut basis kode @types/react , sedangkan <IAction> adalah larik tindakan Antarmuka.

Juga, Visual Studio Code memiliki pemeriksa TypeScript. Jadi, hanya dengan menyorot atau mengarahkan kursor ke kode, cukup pintar untuk menyarankan jenis yang sesuai.

Dengan kata lain, agar kami dapat menggunakan antarmuka kami di seluruh aplikasi kami, kami perlu mengekspornya. Sejauh ini, kami memiliki toko dan antarmuka kami yang menampung jenis objek kami. Sekarang mari kita buat toko kita. Perhatikan bahwa antarmuka lain mengikuti konvensi yang sama seperti yang dijelaskan.

Ambil Episode

Membuat Toko

Untuk mengambil episode kami, kami membutuhkan toko yang menyimpan status awal data dan yang mendefinisikan fungsi peredam kami.

Kami akan menggunakan kait useReducer untuk mengaturnya. Buat file store.tsx di folder src Anda. Salin dan tempel kode berikut ke dalamnya.

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

Berikut ini adalah langkah-langkah yang kami lakukan untuk membuat toko:

  • Dalam mendefinisikan toko kita, kita memerlukan kait useReducer dan API createContext dari React, itulah sebabnya kita mengimpornya.
  • Kami mengimpor IState dan IAction dari ./types/interfaces .
  • Kami mendeklarasikan objek initialState dengan tipe IState , dan properti episode dan favorit, yang keduanya disetel ke array kosong, masing-masing.
  • Selanjutnya, kami membuat variabel Store yang menyimpan metode createContext dan yang diteruskan dengan initialState .

Jenis metode createContext adalah <IState | any> <IState | any> , yang berarti bisa berupa jenis <IState> atau any . Kita akan melihat jenis any yang sering digunakan dalam artikel ini.

  • Selanjutnya, kami mendeklarasikan fungsi reducer dan meneruskan state dan action sebagai parameter. Fungsi reducer memiliki pernyataan sakelar yang memeriksa nilai action.type . Jika nilainya adalah FETCH_DATA , maka ia mengembalikan objek yang memiliki salinan status kita (...state) dan status episode yang menampung muatan tindakan kita.
  • Dalam pernyataan switch, kami mengembalikan status default .

Perhatikan bahwa parameter state dan action dalam fungsi peredam masing-masing memiliki tipe IState dan IAction . Juga, fungsi reducer memiliki tipe IState .

  • Terakhir, kami mendeklarasikan fungsi StoreProvider . Ini akan memberikan semua komponen di aplikasi kami akses ke toko.
  • Fungsi ini mengambil children -anak sebagai penyangga, dan di dalam fungsi StorePrivder , kami mendeklarasikan kait useReducer .
  • Kami merusak state dan dispatch .
  • Untuk membuat toko kami dapat diakses oleh semua komponen, kami memberikan nilai objek yang berisi state dan dispatch .

state yang berisi episode dan status favorit kami akan dapat diakses oleh komponen lain, sedangkan dispatch adalah fungsi yang mengubah status.

  • Kami akan mengekspor Store dan StoreProvider , sehingga dapat digunakan di seluruh aplikasi kami.

Buat Action.ts

Kita harus membuat permintaan ke API untuk mengambil episode yang akan ditampilkan kepada pengguna. Ini akan dilakukan dalam file tindakan. Buat file Action.ts , lalu tempel kode berikut:

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

Pertama, kita perlu mengimpor antarmuka kita agar dapat digunakan dalam file ini. Langkah-langkah berikut diambil untuk membuat tindakan:

  • Fungsi fetchDataAction mengambil alat peraga dispatch sebagai parameter.
  • Karena fungsi kami tidak sinkron, kami akan menggunakan async dan await .
  • Kami membuat variabel ( URL ) yang menyimpan titik akhir API kami.
  • Kami memiliki variabel lain bernama data yang menyimpan respons dari API.
  • Kemudian, kita menyimpan respon JSON di dataJSON , setelah kita mendapatkan respon dalam format JSON dengan memanggil data.json() .
  • Terakhir, kami mengembalikan fungsi pengiriman yang memiliki properti type dan string FETCH_DATA . Ia juga memiliki payload() . _embedded.episodes adalah larik objek episodes dari endpoint kita .

Perhatikan bahwa fungsi fetchDataAction mengambil titik akhir kita, mengonversinya menjadi objek JSON , dan mengembalikan fungsi pengiriman, yang memperbarui status yang dideklarasikan sebelumnya di Store.

Jenis pengiriman yang diekspor diatur ke React.Dispatch . Perhatikan bahwa React.Dispatch adalah tipe standar untuk fungsi pengiriman menurut basis kode @types/react , sedangkan <IAction> adalah larik dari Tindakan Antarmuka.

Komponen Daftar Episode

Untuk mempertahankan kegunaan kembali aplikasi kami, kami akan menyimpan semua episode yang diambil dalam file terpisah, dan kemudian mengimpor file dalam homePage halaman rumah kami.

Di folder components , buat file EpisodesList.tsx , dan salin dan tempel kode berikut ke dalamnya:

 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
  • Kami mengimpor IEpisode dan IProps dari interfaces.tsx .
  • Selanjutnya, kita membuat fungsi EpisodesList yang mengambil props. Alat peraga akan memiliki tipe IProps , sedangkan fungsinya memiliki tipe Array<JSX.Element> .

Visual Studio Code menyarankan agar tipe fungsi kita ditulis sebagai JSX.Element[] .

Visual Studio Code menyarankan jenis (Pratinjau besar)

Sementara Array<JSX.Element> sama dengan JSX.Element[] , Array<JSX.Element> disebut identitas generik. Oleh karena itu, pola generik akan sering digunakan dalam artikel ini.

  • Di dalam fungsi, kami merusak episodes dari props , yang memiliki IEpisode sebagai tipe.

Baca tentang identitas generik, Pengetahuan ini akan dibutuhkan saat kita melanjutkan.

  • Kami mengembalikan alat peraga episodes dan memetakannya untuk mengembalikan beberapa tag HTML.
  • Bagian pertama memegang key , yaitu episode.id , dan className dari episode-box , yang akan dibuat nanti. Kami tahu bahwa episode kami memiliki gambar; karenanya, tag gambar.
  • Gambar memiliki operator ternary yang memeriksa apakah ada episode.image atau episode.image.medium . Lain, kami menampilkan string kosong jika tidak ada gambar yang ditemukan. Juga, kami menyertakan episode.name dalam div.

Di section , kami menunjukkan musim tempat sebuah episode dan nomornya. Kami memiliki tombol dengan teks Fav . Kami telah mengekspor komponen EpisodesList sehingga kami dapat menggunakannya di seluruh aplikasi kami.

Komponen Halaman Beranda

Kami ingin halaman beranda memicu panggilan API dan menampilkan episode menggunakan komponen EpisodesList yang kami buat. Di dalam folder components , buat komponen HomePage , dan salin dan tempel kode berikut ke dalamnya:

 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
  • Kami mengimpor useContext , useEffect , lazy , dan Suspense dari React. Komponen aplikasi yang diimpor adalah landasan di mana semua komponen lain harus menerima nilai toko.
  • Kami juga mengimpor Store , IEpisodeProps , dan FetchDataAction dari file masing-masing.
  • Kami mengimpor komponen EpisodesList menggunakan fitur React.lazy yang tersedia di React 16.6.

React lazy loading mendukung konvensi pemecahan kode. Dengan demikian, komponen EpisodesList kami dimuat secara dinamis, bukannya dimuat sekaligus, sehingga meningkatkan kinerja aplikasi kami.

  • Kami merusak state dan dispatch sebagai alat peraga dari Store .
  • Ampersand (&&) di kait useEffect memeriksa apakah status episode kita empty (atau sama dengan 0). Jika tidak, kami mengembalikan fungsi fetchDataAction .
  • Terakhir, kami mengembalikan komponen App . Di dalamnya, kami menggunakan pembungkus Suspense , dan mengatur fallback ke div dengan loading teks. Ini akan ditampilkan kepada pengguna sementara kami menunggu tanggapan dari API.
  • Komponen EpisodesList akan dipasang ketika data tersedia, dan data yang akan berisi episodes itulah yang kami sebarkan ke dalamnya.

Siapkan Index.txs

Komponen Homepage harus merupakan turunan dari StoreProvider . Kita harus melakukannya di file index . Ganti nama index.js menjadi index.tsx dan rekatkan kode berikut:

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

Kami mengimpor StoreProvider , HomePage , dan index.css dari file masing-masing. 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.

Kami telah datang jauh. 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.

(Pratinjau besar)

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.

Kami akan menambahkan beberapa cuplikan lagi ke EpisodeList.tsx . Salin dan tempel kode yang disorot:

 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

Kami menyertakan togglefavaction , favorites , dan store sebagai props, dan kami merusak state , dispatch dari store. Untuk memilih episode favorit, kami menyertakan metode toggleFavAction dalam acara onClick , dan meneruskan props state , dispatch , dan episode sebagai argumen ke fungsi tersebut.

Terakhir, kami mengulang status favorite untuk memeriksa apakah fav.id (ID favorit) cocok dengan episode.id . Jika ya, kita beralih antara teks Unfav dan Fav . Ini membantu pengguna mengetahui apakah mereka telah memfavoritkan episode itu atau tidak.

Kami semakin dekat dengan akhir. Tetapi kami masih membutuhkan halaman tempat episode favorit dapat ditautkan saat pengguna memilih di antara episode di halaman beranda.

Jika Anda sudah sejauh ini, tepuk punggung Anda.

Komponen Favpage

Di folder components , buat file FavPage.tsx . Salin dan tempel kode berikut ke dalamnya:

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

Untuk membuat logika di balik pemilihan episode favorit, kami telah menulis sedikit kode. Kami mengimpor lazy dan Suspense dari React. Kami juga mengimpor Store , IEpisodeProps , dan toggleFavAction dari file masing-masing.

Kami mengimpor komponen EpisodesList kami menggunakan fitur React.lazy . Terakhir, kami mengembalikan komponen App . Di dalamnya, kami menggunakan pembungkus Suspense , dan mengatur fallback ke div dengan teks pemuatan.

Ini bekerja mirip dengan komponen Homepage . Komponen ini akan mengakses toko untuk mendapatkan episode favorit pengguna. Kemudian, daftar episode diteruskan ke komponen EpisodesList .

Mari tambahkan beberapa cuplikan lagi ke file HomePage.tsx .

Sertakan toggleFavAction dari ../Actions . Sertakan juga metode toggleFavAction sebagai props.

 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

FavPage kami perlu ditautkan, jadi kami memerlukan tautan di header kami di App.tsx . Untuk mencapai ini, kami menggunakan Reach Router, sebuah library yang mirip dengan React Router. William Le menjelaskan perbedaan antara Reach Router dan React Router.

Di CLI Anda, jalankan npm install @reach/router @types/reach__router . Kami memasang kedua jenis perpustakaan Reach Router dan reach-router .

Setelah instalasi berhasil, impor Link dari @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

Kami merusak toko dari useContext . Terakhir, rumah kita akan memiliki Link dan jalur ke / , sedangkan favorit kita memiliki jalur ke /faves .

{state.favourites.length} memeriksa jumlah episode di status favorit dan menampilkannya.

Terakhir, dalam file index.tsx , kita mengimpor komponen FavPage dan HomePage masing-masing, dan membungkusnya di Router .

Salin kode yang disorot ke kode yang ada:

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

Sekarang, mari kita lihat bagaimana ADD_FAV yang diimplementasikan bekerja.

Kode "Tambahkan favorit" berfungsi (Pratinjau besar)

Hapus Fungsi Favorit

Terakhir, kami akan menambahkan fitur "Hapus episode", sehingga ketika tombol diklik, kami beralih antara menambahkan atau menghapus episode favorit. Kami akan menampilkan jumlah episode yang ditambahkan atau dihapus di header.

TOKO

Untuk membuat fungsi "Hapus episode favorit", kami akan menambahkan kasing lain di toko kami. Jadi, buka Store.tsx dan tambahkan kode yang disorot:

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

Kami menambahkan kasus lain bernama REMOVE_FAV dan mengembalikan objek yang berisi salinan initialState kami. Juga, status favorites berisi muatan aksi.

TINDAKAN

Salin kode yang disorot berikut dan tempel di 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) }

Kami mengimpor antarmuka IState dari ./types/interfaces , karena kami harus meneruskannya sebagai tipe ke properti state dalam fungsi toggleFavAction .

Variabel episodeInFav dibuat untuk memeriksa apakah ada episode yang ada dalam status favorites .

Kami memfilter status favorit untuk memeriksa apakah ID favorit tidak sama dengan ID episode. Dengan demikian, dispatchObj ditugaskan kembali jenis REMOVE_FAV dan muatan favWithoutEpisode .

Mari kita lihat hasil dari aplikasi kita.

Kesimpulan

Dalam artikel ini, kita telah melihat cara mengatur TypeScript dalam proyek React, dan cara memigrasikan proyek dari vanilla React ke TypeScript.

Kami juga telah membuat aplikasi dengan TypeScript dan React untuk melihat bagaimana TypeScript digunakan dalam proyek React. Saya percaya Anda dapat mempelajari beberapa hal.

Silakan bagikan umpan balik dan pengalaman Anda dengan TypeScript di bagian komentar di bawah. Saya ingin melihat apa yang Anda dapatkan!

Repositori pendukung untuk artikel ini tersedia di GitHub.

Referensi

  1. “Cara Migrasi Aplikasi React Ke TypeScript,” Joe Previte
  2. “Mengapa Dan Bagaimana Cara Menggunakan TypeScript di Aplikasi React Anda?,” Mahesh Haldar