Fermer

mai 11, 2020

Définition de TypeScript pour les projets Modern React à l'aide de Webpack et Babel


À propos de l'auteur

Blessing Krofegha est un ingénieur logiciel basé à Lagos au Nigeria, avec un ardent désir de contribuer à rendre le Web génial pour tous, en écrivant et en construisant…
En savoir plus sur
Bénédiction

Cet article présente Typescript, un exposant de JavaScript qui présente la fonction de type statique pour repérer les erreurs courantes en tant que codes de développeur, ce qui améliore les performances, d'où des applications d'entreprise robustes. Vous apprendrez également comment configurer efficacement TypeScript dans un projet React en créant une application Money Heist Episode Picker, en explorant TypeScript, des crochets React tels que useReducer, useContext et Reach Router.

À l'ère du développement logiciel, JavaScript peut être utilisé pour développer presque n'importe quel type d'application. Cependant, le fait que JavaScript soit typé dynamiquement pourrait être une préoccupation pour la plupart des grandes entreprises, en raison de sa fonction de vérification de type lâche.

Heureusement, nous n'avons pas à attendre le Comité technique Ecma 39 introduit un système de type statique dans JavaScript. Nous pouvons plutôt utiliser TypeScript.

JavaScript, étant typé dynamiquement, ne connaît pas le type de données d'une variable tant que cette variable n'est pas instanciée au moment de l'exécution. Les développeurs qui écrivent de gros logiciels peuvent avoir tendance à réaffecter une variable, déclarée plus tôt, à une valeur d'un type différent, sans avertissement ni problème, ce qui entraîne des bogues souvent ignorés.

Dans ce didacticiel, nous allons apprendre ce que TypeScript est et comment travailler avec lui dans un projet React. À la fin, nous aurons construit un projet consistant en une application de sélection d'épisode pour l'émission de télévision Money Heist en utilisant TypeScript et les crochets actuels de type React ( useState useEffect useReducer useContext ). Avec ces connaissances, vous pouvez continuer à expérimenter avec TypeScript dans vos propres projets.

Cet article n'est pas une introduction à TypeScript . Par conséquent, nous ne passerons pas par la syntaxe de base de TypeScript et JavaScript. Cependant, vous n'avez pas besoin d'être un expert dans l'un de ces langages pour suivre, car nous allons essayer de suivre le principe KISS (garder les choses simples, stupides).

Qu'est-ce que TypeScript?

En 2019 , TypeScript a été classé septième langage le plus utilisé et cinquième langage à la croissance la plus rapide sur GitHub. Mais qu'est-ce que TypeScript exactement?

Selon la documentation officielle TypeScript est un sur-ensemble typé de JavaScript qui se compile en JavaScript simple. Il est développé et maintenu par Microsoft et la communauté open-source.

«Superset» dans ce contexte signifie que le langage contient toutes les caractéristiques et fonctionnalités de JavaScript, puis certaines. TypeScript est un langage de script typé.

Il offre aux développeurs plus de contrôle sur leur base de code via son annotation de type, ses classes et son interface, évitant ainsi aux développeurs d'avoir à corriger manuellement des bogues gênants dans la console.

TypeScript n'a pas été créé pour modifier JavaScript. Au lieu de cela, il se développe sur JavaScript avec de nouvelles fonctionnalités précieuses. Tout programme écrit en JavaScript simple s'exécutera également comme prévu dans TypeScript, y compris les applications mobiles multiplateformes et les backends dans Node.js.

Cela signifie que vous pouvez également écrire des applications React en TypeScript, comme nous le ferons dans ce didacticiel.

Pourquoi TypeScript?

Peut-être, vous n'êtes pas convaincu d'embrasser la bonté de TypeScript. Examinons quelques-uns de ses avantages.

Moins de bogues

Nous ne pouvons pas éliminer tous les bogues de notre code, mais nous pouvons les réduire. TypeScript vérifie les types au moment de la compilation et génère des erreurs si le type de variable change.

Le fait de pouvoir détecter ces erreurs évidentes mais fréquentes dès le début facilite beaucoup la gestion de votre code avec les types.

La refactorisation est plus facile

Vous voudrez probablement refactoriser pas mal de choses, mais parce qu'elles touchent tellement d'autres codes et de nombreux autres fichiers, vous avez peur de les modifier.

Dans TypeScript, de telles choses peuvent souvent être refactorisées avec juste un clic sur la commande " Renommer le symbole " dans votre environnement de développement intégré (IDE).

Renommer l'application en expApp ( Grand aperçu )

Dans un langage typé dynamiquement tel en tant que JavaScript, le seul moyen de refactoriser plusieurs fichiers en même temps est d'utiliser la fonction traditionnelle de «recherche et remplacement» à l'aide d'expressions régulières (RegExp).

Dans un langage de type statique tel que TypeScript, «rechercher et remplacer» n'est pas » t plus nécessaire. Avec les commandes IDE telles que «Rechercher toutes les occurrences» et «Renommer le symbole», vous pouvez voir toutes les occurrences dans l'application de la fonction, de la classe ou de la propriété d'une interface d'objet.

TypeScript vous aidera à trouver toutes les instances de la refactorisé, renommez-le et vous alertera avec une erreur de compilation au cas où votre code aurait des incompatibilités de type après la refactorisation.

TypeScript a même plus d'avantages que ce que nous avons couvert ici.

Inconvénients de TypeScript

TypeScript n'est certainement pas sans inconvénients, même compte tenu des fonctionnalités prometteuses soulignées ci-dessus.

Un faux sens de la sécurité

La fonction de vérification de type de TypeScript crée souvent un faux sentiment de sécurité chez les développeurs. La vérification de type nous avertit en effet lorsque quelque chose ne va pas avec notre code. Cependant, les types statiques ne réduisent pas la densité globale des bogues.

Par conséquent, la force de votre programme dépendra de votre utilisation de TypeScript, car les types sont écrits par le développeur et non vérifiés au moment de l'exécution.

Si vous êtes Si vous cherchez à TypeScript pour réduire vos bogues, veuillez plutôt envisager un développement piloté par les tests .

Système de typage compliqué

Le système de typage, bien qu'il soit un excellent outil à bien des égards, peut parfois être un peu compliqué. Cet inconvénient provient du fait qu'il est entièrement interopérable avec JavaScript, ce qui laisse encore plus de place à la complication.

Cependant, TypeScript est toujours JavaScript, donc la compréhension de JavaScript est importante .

Quand utiliser TypeScript? [19659019] Je vous conseille d'utiliser TypeScript dans les cas suivants:

  • Si vous souhaitez créer une application qui sera maintenue sur une longue période je vous recommande fortement de commencer par TypeScript, car il favorise le code auto-documenté, aidant ainsi les autres développeurs à comprendre facilement votre code lorsqu'ils rejoignent votre base de code.
  • Si vous devez créer une bibliothèque pensez à l'écrire en TypeScript. Il aidera les éditeurs de code à suggérer les types appropriés aux développeurs qui utilisent votre bibliothèque.

Dans les dernières sections, nous avons équilibré les avantages et les inconvénients de TypeScript. Passons à la tâche courante: configuration de TypeScript dans un projet React moderne .

Mise en route

Il existe plusieurs façons de configurer TypeScript dans un projet React. Dans ce didacticiel, nous n'en couvrirons que deux.

Méthode 1: Créer React App + TypeScript

Il y a environ deux ans, l'équipe React a publié Create React App 2.1, avec Prise en charge de TypeScript . Ainsi, vous n'aurez peut-être jamais à faire de gros efforts pour intégrer TypeScript dans votre projet.

Annonce de TypeScript dans Create React App ( Grand aperçu )

Pour démarrer un nouveau projet Create React App, vous pouvez l'exécuter…

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

… ou ceci:

 fil crée react-app my-app --folder-name

Pour ajouter TypeScript à un projet Create React App, installez-le d'abord et ses @types respectifs :

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

… ou:

 fil ajouter typescript @ types / node @ types / react @ types / react-dom @ types / jest

Ensuite, renommez les fichiers (par exemple, index.js en index.tsx ) et redémarrez votre serveur de développement !

Méthode 2: Configurer TypeScript avec Webpack

Webpack est un bundle de modules statiques pour les applications JavaScript. Il prend tout le code de votre application et le rend utilisable dans un navigateur Web. Les modules sont des morceaux de code réutilisables construits à partir du JavaScript de votre application, node_modules des images et des styles CSS, qui sont conçus pour être facilement utilisés sur votre site Web.

Créer un nouveau projet

Commençons par création d'un nouveau répertoire pour notre projet:

 mkdir react-webpack
cd react-webpack

Nous utiliserons npm pour initialiser notre projet:

 npm init -y

La commande ci-dessus va générer un fichier package.json avec quelques valeurs par défaut. Ajoutons également quelques dépendances pour le webpack, TypeScript et certains modules spécifiques à React.

Installation des packages

Enfin, nous devons installer les packages nécessaires. Ouvrez votre interface de ligne de commande (CLI) et exécutez ceci:

 #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 -serveur

#installing Dependencies
npm installer react react-dom

Ajoutons également manuellement quelques fichiers et dossiers différents dans notre dossier react-webpack :

  1. Ajoutez webpack.config.js pour ajouter des configurations liées aux webpack. [19659041] Ajoutez tsconfig.json pour toutes nos configurations TypeScript.
  2. Ajoutez un nouveau répertoire, src .
  3. Créez un nouveau répertoire, composants dans le dossier src .
  4. Enfin, ajoutez index.html App.tsx et index.tsx dans le composants dossier.

Structure du projet

Ainsi, notre structure de dossiers ressemblera à ceci:

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

Commencez à ajouter du code

Commençons par index.html :




  
  
   Configuration de React-Webpack 


  

Cela créera le code HTML, avec une div vide avec un ID de sortie .

Ajoutons le code à notre composant React App.tsx ]:

 importez * en tant que React de "react";
interface d'exportation HelloWorldProps {
  userName: chaîne;
  lang: string;
}
export const App = (accessoires: HelloWorldProps) => (
         

           Salut {props.userName} de React! Bienvenue sur {props.lang}!          

       );

Nous avons créé un objet d'interface et l'avons nommé HelloWorldProps avec userName et lang ayant une chaîne de type . [19659005] Nous avons transmis les accessoires à notre composant App et l'avons exporté.

Maintenant, mettons à jour le code dans index.tsx :

 import * as Réagir de "réagir";
importer * comme ReactDOM à partir de "react-dom";
importer {App} à partir de "./App";
ReactDOM.render (
  ,
  document.getElementById ("sortie")
);

Nous venons d'importer le composant App dans index.tsx . Lorsque webpack voit un fichier avec l'extension .ts ou .tsx il transpile ce fichier à l'aide de la bibliothèque awesome-typescript-loader .

TypeScript Configuration

Nous ajouterons ensuite une configuration à tsconfig.json :

 {
  "options de compilation": {
    "jsx": "réagir",
    "module": "commonjs",
    "noImplicitAny": vrai,
    "outDir": "./build/",
    "preserveConstEnums": vrai,
    "removeComments": vrai,
    "sourceMap": vrai,
    "cible": "es5"
  },
  "inclure": [
    "src/components/index.tsx"
  ]
}

Examinons également les différentes options que nous avons ajoutées à tsconfig.json :

  • compilerOptions
    Représente les différentes options du compilateur.
  • jsx: react
    Ajoute la prise en charge de JSX dans les fichiers .tsx .
  • lib
    Ajoute une liste de fichiers de bibliothèque à la compilation (par exemple, l'utilisation de es2015 nous permet d'utiliser la syntaxe ECMAScript 6).
  • module
    Génère le code du module.
  • noImplicitAny
    Génère des erreurs pour les déclarations avec un type implicite de tout type .
  • outDir
    Représente le répertoire de sortie.
  • sourceMap
    Génère un fichier .map qui peut être très utile pour déboguer l'application.
  • target
    Représente la version ECMAScript cible vers laquelle transpiler notre code (nous pouvons ajouter une version en fonction des exigences spécifiques de notre navigateur).
  • incluent
    Utilisé pour spécifier la liste de fichiers à inclure.

Configuration de Webpack

Ajoutons une configuration de Webpack à webpack.config.js .

 const path = require ("path");
const HtmlWebpackPlugin = require ("html-webpack-plugin");
const MiniCssExtractPlugin = require ("mini-css-extract-plugin");
module.exports = {
  entrée: "./src/components/index.tsx",
  cible: "web",
  mode: "développement",
  production: {
    path: path.resolve ( __ dirname, "build"),
    nom de fichier: "bundle.js",
  },
  résoudre: {
    extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
  },
  module: {
    règles: [
      {
        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",
    }),
  ],
};

Voyons les différentes options que nous avons ajoutées à webpack.config.js :

  • entrée
    Cela spécifie le point d'entrée de notre application. Il peut s'agir d'un seul fichier ou d'un tableau de fichiers que nous souhaitons inclure dans notre build.
  • output
    Il contient la configuration de sortie. L'application examine cela lors de la tentative de sortie du code groupé de notre projet sur le disque. Le chemin d'accès représente le répertoire de sortie pour le code à sortir, et le nom de fichier représente le nom de fichier pour le même. Il est généralement nommé bundle.js .
  • résoudre
    Webpack examine cet attribut pour décider de regrouper ou d'ignorer le fichier. Ainsi, dans notre projet, webpack considérera les fichiers avec les extensions .js .jsx .json .ts et .tsx pour le regroupement.
  • module
    Nous pouvons permettre à webpack de charger un fichier particulier à la demande de l'application, à l'aide de chargeurs. Il prend un objet de règles qui spécifie que:

    • tout fichier se terminant par l'extension .tsx ou .ts doit utiliser awesome-typescript-loader pour être chargés;
    • les fichiers se terminant par l'extension .js doivent être chargés avec source-map-loader ;
    • les fichiers se terminant par .css l'extension doit être chargée avec css-loader .
  • plugins
    Webpack a ses propres limites, et il fournit des plugins pour les surmonter et étendre ses capacités. Par exemple, html-webpack-plugin crée un fichier de modèle rendu au navigateur à partir du fichier index.html dans le fichier ./ src / component / index.html .

MiniCssExtractPlugin affiche le fichier parent CSS du fichier de l'application.

Ajout de scripts à package.json

Nous pouvons ajouter différents scripts pour construire React applications dans notre package.json fichier:

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

Maintenant, exécutez npm start dans votre CLI. Si tout s'est bien passé, vous devriez voir ceci:

Sortie de configuration React-Webpack ( Grand aperçu )

Si vous avez un talent pour webpack, clonez le référentiel pour cette configuration et utilisez-la dans vos projets.

Création de fichiers

Créez un dossier src et un fichier index.tsx . Ce sera le fichier de base qui rend React.

Maintenant, si nous exécutons npm start il exécutera notre serveur et ouvrira un nouvel onglet. L'exécution de npm run build créera un webpack pour la production et créera un dossier de build pour nous.

Nous avons vu comment configurer TypeScript à partir de zéro à l'aide de l'application Create React et de la méthode de configuration du webpack.

L'une des façons les plus rapides d'obtenir une compréhension complète de TypeScript consiste à convertir l'un de vos projets Vanilla React existants en TypeScript. Malheureusement, l'adoption incrémentielle de TypeScript dans un projet Vanilla React existant est stressante car elle implique d'avoir à éjecter ou renommer tous les fichiers, ce qui entraînerait des conflits et une gigantesque demande de pull si le projet appartenait à une grande équipe.

Ensuite, nous verrons comment migrer facilement un projet React vers TypeScript.

Migrer une application Create React existante vers TypeScript

Pour rendre ce processus plus facile à gérer, nous le décomposerons en étapes, ce qui nous permettra de migrer en morceaux individuels. Voici les étapes à suivre pour migrer notre projet:

  1. Ajouter TypeScript et types.
  2. Ajouter tsconfig.json .
  3. Commencer petit.
  4. Renommer l'extension des fichiers en .tsx .
1. Ajouter TypeScript au projet

Tout d'abord, nous devons ajouter TypeScript à notre projet. En supposant que votre projet React a été démarré avec Create React App, nous pouvons exécuter ce qui suit:

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

# Utilisation de fil
fil ajouter typescript @ types / node @ types / react @ types / react-dom @ types / jest

Notez que nous n'avons encore rien changé en TypeScript. Si nous exécutons la commande pour démarrer le projet localement ( npm start ou yarn start ), rien ne change. Si tel est le cas, tant mieux! Nous sommes prêts pour la prochaine étape.

2. Ajouter le fichier tsconfig.json

Avant de tirer parti de TypeScript, nous devons le configurer via le fichier tsconfig.json . La façon la plus simple de commencer est d'échafauder un en utilisant cette commande:

 npx tsc --init

Cela nous donne quelques notions de base, avec beaucoup de code commenté. Maintenant, remplacez tout le code dans tsconfig.json par ceci:

 {
    "options de compilation": {
      "jsx": "réagir",
      "module": "commonjs",
      "noImplicitAny": vrai,
      "outDir": "./build/",
      "preserveConstEnums": vrai,
      "removeComments": vrai,
      "sourceMap": vrai,
      "cible": "es5"
    },
    "inclure": [
      "./src/**/**/*"
    ]
  }

Configuration TypeScript

Examinons également les différentes options que nous avons ajoutées à tsconfig.json :

  • compilerOptions
    Représente les différentes options du compilateur.

    • target
      Traduit les nouvelles constructions JavaScript vers une version plus ancienne, comme ECMAScript 5.
    • lib
      Ajoute une liste de fichiers de bibliothèque à la compilation (par exemple, l'utilisation de es2015 nous permet d'utiliser la syntaxe ECMAScript 6.)
    • jsx: react
      Ajoute la prise en charge de JSX dans les fichiers .tsx .
    • lib
      Ajoute une liste de fichiers de bibliothèque à la compilation (par exemple, l'utilisation de es2015 nous permet d'utiliser la syntaxe ECMAScript 6.)
    • module
      Génère le code du module.
    • noImplicitAny
      Utilisé pour générer des erreurs pour les déclarations avec un type implicite de tout type .
    • outDir
      Représente le répertoire de sortie.
    • sourceMap
      Génère un fichier .map qui peut être très utile pour déboguer notre application.
    • incluent
      Permet de spécifier la liste de fichiers à inclure.

Les options de configuration varient en fonction de la demande d'un projet. Vous devrez peut-être vérifier la feuille de calcul des options TypeScript pour déterminer ce qui conviendrait à votre projet.

Nous n'avons pris que les mesures requises pour préparer les choses. Notre prochaine étape consiste à migrer un fichier vers TypeScript.

3. Commencez avec un composant simple

Tirez parti de la capacité de TypeScript à être progressivement adopté. Allez un fichier à la fois à votre rythme. Faites ce qui a du sens pour vous et votre équipe. N'essayez pas de vous attaquer à tout cela en même temps.

Pour convertir cela correctement, nous devons faire deux choses:

  1. Changer l'extension du fichier en .tsx .
  2. Ajouter l'annotation de type (qui nécessiterait une certaine connaissances TypeScript ).
4.Renommer les extensions de fichier à .tsx

Dans une grande base de code, il peut sembler fatigant de renommer les fichiers individuellement.

Renommer plusieurs fichiers sur macOS

Renommer plusieurs fichiers peut être une perte de temps. Voici comment vous pouvez le faire sur un Mac. Faites un clic droit (ou Ctrl + clic, ou cliquez avec deux doigts simultanément sur le trackpad si vous utilisez un MacBook) sur le dossier qui contient les fichiers que vous souhaitez renommer. Cliquez ensuite sur «Révéler dans le Finder». Dans le Finder, sélectionnez tous les fichiers que vous souhaitez renommer. Cliquez avec le bouton droit sur les fichiers sélectionnés et choisissez «Renommer les éléments X…». Ensuite, vous verrez quelque chose comme ceci:

Renommer des fichiers sur un Mac ( Grand aperçu )

Insérez la chaîne souhaitée pour trouver, et la chaîne avec laquelle vous souhaitez remplacer cette chaîne trouvée, et appuyez sur "Renommer".

Renommer plusieurs fichiers sous Windows

Renommer plusieurs fichiers sous Windows dépasse le cadre de ce didacticiel, mais un guide complet est disponible.
Vous obtiendrez généralement des erreurs après avoir renommé les fichiers; il vous suffit d'ajouter les annotations de type. Vous pouvez rafraîchir ce point dans la documentation .

Nous avons expliqué comment configurer TypeScript dans une application React. Maintenant, créons une application de sélection d'épisode pour Money Heist en utilisant TypeScript.

Nous ne couvrirons pas les types de base de TypeScript. Il est nécessaire de parcourir la documentation avant de continuer dans ce didacticiel.

Temps de construction

Pour rendre ce processus moins intimidant, nous allons le décomposer en étapes, ce qui nous permettra de construire l'application en morceaux individuels. Voici toutes les étapes que nous prendrons pour créer le sélecteur d'épisode Money Heist :

  • Échafaudez une application Create React.
  • Récupérez les épisodes.
    • Créez les types appropriés. et interfaces pour nos épisodes dans interface.ts .
    • Configurer le magasin pour la récupération des épisodes dans store.tsx .
    • Créer l'action pour récupérer les épisodes dans action.ts .
    • Créez un composant EpisodeList.tsx qui contient les épisodes récupérés.
    • Importez le composant EpisodesList dans notre page d'accueil à l'aide de ] React Lazy and Suspense .
  • Ajouter des épisodes.
    • Configurer le magasin pour ajouter des épisodes dans store.tsx .
    • Créer l'action pour ajouter des épisodes dans action.ts .
  • Supprimer les épisodes.
    • Configurer le magasin pour la suppression des épisodes dans store.tsx . [19659041] Créez l'action de suppression des épisodes dans action.ts .
  • Épisode préféré.
    • Importez EpisodesList composant de l'épisode préféré.
    • Rendu Liste des épisodes dans l'épisode préféré.
  • Utilisation de Reach Router pour la navigation.

Configurer React

La manière la plus simple de configurer React consiste à utiliser Créer React App . Créer une application React est un moyen officiellement pris en charge pour créer des applications React d'une seule page. Il offre une configuration de construction moderne sans configuration.

Nous nous en servirons pour amorcer l'application que nous allons créer. Depuis votre CLI, exécutez la commande ci-dessous:

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

Une fois l'installation terminée, démarrez le serveur React en exécutant npm start .

Page de démarrage de React ( Grand aperçu )

Comprendre les interfaces et les types en tapuscrit [19659019] Les interfaces en TypeScript sont utilisées lorsque nous devons donner des types aux propriétés des objets. Par conséquent, nous utiliserions des interfaces pour définir nos types.

 interface Employee {
  nom: chaîne,
  rôle: chaîne
  salaire: nombre
}
const bestEmployee: Employee = {
  nom: «John Doe»,
  rôle: 'Développeur IOS',
  salaire: '8500 $' // remarquez que nous utilisons une chaîne
}

Lors de la compilation du code ci-dessus, nous verrions cette erreur: «Les types de propriété salaire sont incompatibles. La chaîne de type ne peut pas être affectée au numéro de type . »

De telles erreurs se produisent dans TypeScript lorsqu'une propriété ou une variable se voit affecter un type autre que le type défini. Plus précisément, l'extrait ci-dessus signifie que la propriété salaire a été affectée à un type de chaîne au lieu d'un type numéro .

Créons une interface . ts dans notre dossier src . Copiez et collez ce code dedans:

 / **
| ------------------------------------------------- -
| Toutes les interfaces!
| ------------------------------------------------- -
* /
interface d'exportation IEpisode {
  airdate: string
  piste d'atterrissage: chaîne
  temps d'antenne: chaîne
  id: nombre
  image: {medium: string; original: string}
  nom: chaîne
  nombre: nombre
  runtime: nombre
  saison: nombre
  résumé: chaîne
  url: chaîne
}
interface d'exportation IState {
  épisodes: Array 
  favoris: Array 
}
interface d'exportation IAction {
  type: chaîne
  charge utile: Array  | tout
}
type d'exportation Dispatch = React.Dispatch 
type d'exportation FavAction = (
  état: IState,
  expédition: Dispatch,
  épisode: IEpisode
) => IAction

interface d'exportation IEpisodeProps {
  épisodes: Array 
  store: {état: IState; dispatch: Dispatch}
  toggleFavAction: FavAction
  favoris: Array 
}
interface d'exportation IProps {
  épisodes: Array 
  store: {état: IState; dispatch: Dispatch}
  toggleFavAction: FavAction
  favoris: Array 
}

C'est une bonne pratique d'ajouter un «I» au nom de l'interface. Cela rend le code lisible. Cependant, vous pouvez décider de l'exclure.

Interface IEpisode

Notre API renvoie un ensemble de propriétés telles que airdate airstamp ] airtime id image nom numéro runtime saison résumé et url . Par conséquent, nous avons défini une interface IEpisode et défini les types de données appropriés sur les propriétés de l'objet.

Interface IState

Notre interface IState a ] épisodes et propriétés favorites respectivement, et une interface Array .

IAction

L'interface IAction interface les propriétés sont de type charge utile et . La propriété type a un type chaîne, tandis que la charge utile a un type Array | .

Notez que Array | any signifie un tableau de l'interface d'épisode ou de n'importe quel type.

Le type Dispatch est défini sur React.Dispatch et une interface . Notez que React.Dispatch est le type standard pour la fonction dispatch selon la base de code @ types / react tandis que est un tableau de l'action Interface.

De plus, Visual Studio Code possède un vérificateur TypeScript. Donc, en mettant simplement en surbrillance ou en survolant le code, il est assez intelligent pour suggérer le type approprié.

En d'autres termes, pour que nous puissions utiliser notre interface dans toutes nos applications, nous devons l'exporter. Jusqu'à présent, nous avons notre magasin et nos interfaces qui contiennent le type de notre objet. Créons maintenant notre magasin.
Notez que les autres interfaces suivent les mêmes conventions que celles expliquées.

Récupération des épisodes

Création d'un magasin

Pour récupérer nos épisodes, nous avons besoin d'un magasin qui contient l'état initial du

Nous utiliserons le crochet useReducer pour configurer cela. Créez un fichier store.tsx dans votre dossier src . Copiez et collez-y le code suivant.

 import React, {useReducer, createContext} de 'react'
importer {IState, IAction} depuis './types/interfaces'
const initialState: IState = {
  épisodes: [],
  favoris: []
}
export const Store = createContext  (initialState)
const reducer = (état: IState, action: IAction): IState => {
  commutateur (action.type) {
    cas 'FETCH_DATA':
      retourner {... état, épisodes: action.payload}
    défaut:
      état de retour
  }
}
export const StoreProvider = ({children}: JSX.ElementChildrenAttribute): JSX.Element => {
  const [state, dispatch] = useReducer (réducteur, initialState)
  retour  {enfants} 
}
 

Voici les étapes que nous avons suivies pour créer le magasin:

  • Pour définir notre magasin, nous avons besoin du [useReducer] (https://reactjs.org/docs/hooks-reference.html #usereducer) hook et l'API createContext de React, c'est pourquoi nous l'avons importé.
  • Nous avons importé IState et IAction de ] ./ types / interfaces .
  • Nous avons déclaré un objet initialState avec un type de IState et les propriétés des épisodes et des favoris, qui sont toutes les deux définies comme vides.
  • Ensuite, nous avons créé une variable Store qui contient la méthode createContext et qui passe la initialState .

La La ] Le type de méthode createContext est ce qui signifie qu'il peut s'agir d'un type de ou de . We will see the any type used often in this article.

Note that the state and action parameters in the reducer function have IState and IAction types, respectively. Also, the reducer function has a type of IState.

The state that contains our episodes and favorites state will be made accessible by other components, while the dispatch is a function that changes the state.

Create Action.ts

We’ll need to make requests to the API to fetch the episodes that will be shown the user. This will be done in an action file. Create an Action.ts file, and then paste the following code:

First, we need to import our interfaces so that they can be used in this file. The following steps were taken to create the action:

Note that the fetchDataAction function fetches our endpoint, converts it to JSON objects, and returns the dispatch function, which updates the state declared earlier in the Store.

The exported dispatch type is set to React.Dispatch. Note that React.Dispatch is the standard type for the dispatch function according to the @types/react code base, while is an array of the Interface Action.

EpisodesList Component

In order to maintain the reusability of our app, we will keep all fetched episodes in a separate file, and then import the file in our homePage component.

In the components folder, create an EpisodesList.tsx file, and copy and paste the following code to it:

Visual Studio Code suggests that our function type be written as JSX.Element[].

Visual Studio Code suggests a type (Large preview)

While Array is equal to JSX.Element[]Array is called the generic identity. Hence, the generic pattern will be used often in this article.

  • Inside the function, we destructure the episodes from propswhich has the IEpisode as a type.

Read about the generic identityThis knowledge will be needed as we proceed.

  • We returned the episodes props and mapped through it to return a few HTML tags.
  • The first section holds the keywhich is episode.idand a className of episode-boxwhich will be created later. We know that our episodes have images; hence, the image tag.
  • The image has a ternary operator that checks if there’s either an episode.image or an episode.image.medium. Else, we display an empty string if no image is found. Also, we included the episode.name in a div.

In sectionwe show the season that an episode belongs to and its number. We have a button with the text Fav. We’e exported the EpisodesList component so that we can use it across our app.

Home Page Component

We want the home page to trigger the API call and display the episodes using the EpisodesList component we created. Inside the components folder, create the HomePage component, and copy and paste the following code to it:

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(() => 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 (
    
      <Suspense fallback={
loading...
}>         
)   } export default HomePage
  • We import useContextuseEffectlazyand Suspense from React. The imported app component is the bedrock upon which all other components must receive the value of the store.
  • We also import StoreIEpisodePropsand FetchDataAction from their respective files.
  • We import the EpisodesList component using the React.lazy feature available in React 16.6.

React lazy loading supports the code-splitting convention. Thus, our EpisodesList component is loaded dynamically, instead of being loaded at once, thereby improving the performance of our app.

  • We destructure the state and dispatch as props from the Store.
  • The ampersand (&&) in the useEffect hook checks if our episodes state is empty (or equal to 0). Else, we return the fetchDataAction function.
  • Lastly, we return the App component. Inside it, we use the Suspense wrapper, and set fallback to a div with the loading text. This will be displayed to the user while we await the response from the API.
  • The EpisodesList component will mount when the data is available, and the data that will contain the episodes is what we spread into it.

Set Up Index.txs

The Homepage component needs to be a child of the StoreProvider. We’ll have to do that in the index file. Rename index.js to index.tsx and paste the following code:

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

We import StoreProviderHomePageand index.css from their respective files.
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.

We have come a long way. 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 {
  marge: 0;
  rembourrage: 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;
  en haut: 0;
}

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

(Large preview)

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(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 {children}
}

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

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.

We will add some more snippets to EpisodeList.tsx. Copy and paste the highlighted code:

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

  return episodes.map((episode: IEpisode) => {
    return (
      
{`Money
{episode.name}
Seasion: {episode.season} Number: {episode.number}           
          <button             type='button'
onClick={() => toggleFavAction(state, dispatch, episode)}           >             {favourites.find((fav: IEpisode) => fav.id === episode.id)               ? 'Unfav'               : 'Fav'}
)   }) } export default EpisodesList

We include togglefavactionfavoritesand store as props, and we destructure statea dispatch from the store. In order to select our favorite episode, we include the toggleFavAction method in an onClick event, and pass the statedispatch and episode props as arguments to the function.

Lastly, we loop through the favorite state to check if fav.id(favorite ID) matches the episode.id. If it does, we toggle between the Unfav and Fav text. This helps the user know if they have favorited that episode or not.

We are getting close to the end. But we still need a page where favorite episodes can be linked to when the user chooses among the episodes on the home page.

If you’ve gotten this far, give yourself a pat on the back.

Favpage Component

In the components folder, create a FavPage.tsx file. Copy and paste the following code to it:

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(() => 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 (
    
      <Suspense fallback={
loading...
}>         
) }

To create the logic behind choosing favorite episodes, we’ve written a little code. We import lazy and Suspense from React. We also import StoreIEpisodePropsand toggleFavAction from their respective files.

We import our EpisodesList component using the React.lazy feature. Lastly, we return the App component. Inside it, we use the Suspense wrapper, and set a fallback to a div with the loading text.

This works similar to the Homepage component. This component will access the store to obtain the episodes the user has favorited. Then, the list of episodes gets passed to the EpisodesList component.

Let’s add a few more snippets to the HomePage.tsx file.

Include the toggleFavAction from ../Actions. Also include the toggleFavAction method as 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(() => 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 (
    
      <Suspense fallback={
loading...
}>         
)   } export default HomePage

Our FavPage needs to be linked, so we need a link in our header in App.tsx. To achieve this, we use Reach Routera library similar to React Router. William Le explains the differences between Reach Router and React Router.

In your CLI, run npm install @reach/router @types/reach__router. We are installing both the Reach Router library and reach-router types.

Upon successful installation, import Link from @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 (
    
      

Money Heist

Pick your favourite episode

        
          Home Favourite(s): {state.favourites.length}         
      {children}        )  } export default App

We destructure the store from useContext. Lastly, our home will have a Link and a path to /while our favorite has a path to /faves.

{state.favourites.length} checks for the number of episodes in the favorites states and displays it.

Finally, in our index.tsx file, we import the FavPage and HomePage components, respectively, and wrap them in the Router.

Copy the highlighted code to the existing code:

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

Now, let’s see how the implemented ADD_FAV works.

The “Add favorite” code works (Large preview)

Remove Favorite Functionality

Finally, we will add the “Remove episode feature”, so that when the button is clicked, we toggle between adding or removing a favorite episode. We will display the number of episodes added or removed in the header.

STORE

To create the “Remove favorite episode” functionality, we will add another case in our store. So, go over to Store.tsx and add the highlighted code:

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

We add yet another case named REMOVE_FAV and return an object containing the copy of our initialState. Also, the favorites state contains the action payload.

ACTION

Copy the following highlighted code and paste it in action.ts:

import { IAction, IEpisode, IState, Dispatch } from './types/interfaces'
export const fetchDataAction = async (dispatch: Dispatch) => {
  const URL =
    'https://api.tvmaze.com/singlesearch/shows?q=la-casa-de-papel&embed=episodes'

  const data = await fetch(URL)
  const dataJSON = await data.json()
  return dispatch({
    type: 'FETCH_DATA',
    payload: dataJSON._embedded.episodes
  })
}
//Add IState withits type
export const toggleFavAction = (state: IState, dispatch: any, episode: IEpisode | any): IAction => {
  const episodeInFav = state.favourites.includes(episode)
  let dispatchObj = {
    type: 'ADD_FAV',
    payload: episode
  }
  if (episodeInFav) {
    const favWithoutEpisode = state.favourites.filter(
      (fav: IEpisode) => fav.id !== episode.id
    )
    dispatchObj = {
      type: 'REMOVE_FAV',
      payload: favWithoutEpisode
    }
  }
  return dispatch(dispatchObj)
}

We import the IState interface from ./types/interfacesbecause we’ll need to pass it as the type to the state props in the toggleFavAction function.

An episodeInFav variable is created to check if there’s an episode that exists in the favorites state.

We filter through the favorites state to check if a favorite ID doesn’t equal an episode ID. Thus, the dispatchObj is reassigned a type of REMOVE_FAV and a payload of favWithoutEpisode.

Let’s preview the result of our app.

Conclusion

In this article, we’ve seen how to set up TypeScript in a React project, and how to migrate a project from vanilla React to TypeScript.

We’ve also built an app with TypeScript and React to see how TypeScript is used in React projects.
I trust you were able to learn a few things.

Please do share your feedback and experiences with TypeScript in the comments section below. I’d love to see what you come up with!

The supporting repository for this article is available on GitHub.

References

  1. How To Migrate A React App To TypeScript,” Joe Previte
  2. Why And How To Use TypeScript In Your React App?,” Mahesh Haldar
Smashing Editorial(ks, ra, il, al)




Source link