Maison >interface Web >js tutoriel >Une plongée profonde dans Redux
Points de base
Construire une application moderne avec état est une tâche complexe. À mesure que l'État change, l'application devient imprévisible et difficile à maintenir. C'est là que Redux entre en jeu. Redux est une bibliothèque légère pour gérer l'état. Considérez-le comme une machine d'état.
Dans cet article, j'explorerai en profondeur les conteneurs d'État de Redux en créant un moteur de traitement de la paie. L'application stockera la masse salariale avec tous les extras tels que les bonus et les options d'achat d'actions. J'utiliserai Pure JavaScript et TypeScript pour la vérification de type pour garder la solution simple. Étant donné que Redux est très facile à tester, j'utiliserai également Jest pour vérifier l'application.
Dans ce tutoriel, je suppose que vous avez une certaine compréhension de JavaScript, de nœud et de NPM.
Tout d'abord, vous pouvez initialiser cette application avec NPM:
<code class="language-bash">npm init</code>
Lorsque vous demandez des commandes de test, continuez à utiliser la plaisanterie. Cela signifie que NPM T commencera à plaisanter et exécutera tous les tests unitaires. Le fichier principal sera index.js pour rester simple. N'hésitez pas à répondre au reste des questions Init NPM.
J'utiliserai TypeScript pour effectuer la vérification de type et déterminer le modèle de données. Cela aide à conceptualiser ce que nous essayons de construire.
pour commencer avec TypeScript:
<code class="language-bash">npm i typescript --save-dev</code>
Je vais mettre certaines des dépendances dans le flux de travail de développement dans DevDependces. Cela montre clairement quelles dépendances sont préparées pour les développeurs et quelles dépendances seront utilisées dans les environnements de production. Une fois que TypeScript est prêt, ajoutez un script de démarrage dans package.json:
<code class="language-json">"start": "tsc && node .bin/index.js"</code>
Créez un fichier index.ts dans le dossier SRC. Cela sépare le fichier source du reste du projet. Si vous commencez NPM, la solution ne sera pas exécutée. En effet, vous devez configurer TypeScript.
Créez un fichier tsconfig.json avec la configuration suivante:
<code class="language-json">{ "compilerOptions": { "strict": true, "lib": ["esnext", "dom"], "outDir": ".bin", "sourceMap": true }, "files": [ "src/index" ] }</code>
J'aurais pu mettre cette configuration dans l'argument de la ligne de commande TSC. Par exemple, TSC SRC / INDEX.TS --TRICT .... Mais mettre tout cela dans un fichier séparé est beaucoup plus clair. Notez que le script de démarrage dans package.json ne nécessite qu'une commande TSC.
Voici quelques options de compilateur raisonnables qui nous donneront un bon point de départ et ce que chaque option signifie:
Parce que j'utiliserai la plaisanterie pour les tests unitaires, je continuerai à l'ajouter:
<code class="language-bash">npm init</code>
La dépendance TS-Jest ajoute la vérification du type pour le cadre de test. Une chose à noter est d'ajouter une configuration de plaisanterie dans package.json:
<code class="language-bash">npm i typescript --save-dev</code>
Cela permet au Framework de test de récupérer les fichiers TypeScript et de savoir comment les convertir. Une belle fonctionnalité est que vous pouvez faire la vérification de type lors des tests unitaires. Pour vous assurer que ce projet est prêt, créez un dossier __tests__ contenant un fichier index.test.ts. Ensuite, un contrôle d'assainissement est effectué. Par exemple:
<code class="language-json">"start": "tsc && node .bin/index.js"</code>
exécuter désormais NPM Start et NPM T ne provoquera aucune erreur. Cela nous dit que nous pouvons maintenant commencer à construire des solutions. Mais avant de faire cela, ajoutons Redux au projet:
<code class="language-json">{ "compilerOptions": { "strict": true, "lib": ["esnext", "dom"], "outDir": ".bin", "sourceMap": true }, "files": [ "src/index" ] }</code>
Cette dépendance sera utilisée dans les environnements de production. Par conséquent, il n'est pas nécessaire de l'inclure avec --Save-DEV. Si vous vérifiez votre package.json, ce sera en dépendances.
Le moteur de paie contiendra les éléments suivants: salaires, remboursements, bonus et options d'achat d'actions. Dans Redux, vous ne pouvez pas mettre à jour le statut directement. Au lieu de cela, une action est prévue pour informer le stockage de toute nouvelle modification.
donc cela laisse le type d'opération suivant:
<code class="language-bash">npm i jest ts-jest @types/jest @types/node --save-dev</code>
Le type d'exploitation PAY_DAY peut être utilisé pour émettre des chèques le jour de paie et suivre l'historique des salaires. Ces types d'opérations guident le reste de la conception alors que nous perfectionnons le moteur de paie. Ils capturent des événements dans le cycle de vie de l'État, comme la fixation du montant du salaire de base. Ces événements d'action peuvent être joints à n'importe quel contenu, qu'il s'agisse d'un événement de clic ou d'une mise à jour de données. Les types d'opération Redux sont abstraits d'où provient la planification. Le conteneur d'état peut s'exécuter sur le client et / ou sur le serveur.
En utilisant la théorie des types, je déterminerai le modèle de données basé sur les données de l'état. Pour chaque opération de paie, comme le type d'opération et le montant facultatif. Le montant est facultatif car Pay_day ne nécessite pas de fonds pour traiter la paie. Je veux dire, il peut facturer aux clients, mais l'ignorer pour l'instant (peut-être introduit dans la deuxième édition).
Par exemple, mettez-le dans src / index.ts:
<code class="language-json">"jest": { "preset": "ts-jest" }</code>
Pour l'état de la paie, nous avons besoin d'un attribut pour le salaire de base, le bonus, etc. Nous utiliserons également ce statut pour maintenir l'historique des salaires.
Cette interface TypeScript doit faire:
<code class="language-bash">npm init</code>
Pour chaque propriété, notez que TypeScript utilise un côlon pour spécifier le type. Par exemple ,: numéro. Cela détermine le contrat de type et ajoute une prévisibilité au vérificateur de type. Redux peut être amélioré à l'aide d'un système de type avec des déclarations de type explicite. En effet, les conteneurs d'état Redux sont conçus pour un comportement prévisible.
Cette idée n'est ni folle ni radicale. Apprentissage Redux Chapitre 1 (Les membres de SitePoint Premium uniquement) explique bien cela.
À mesure que l'application change, la vérification du type ajoute une prévisibilité supplémentaire. À mesure que les applications se développent, la théorie des types aide également à simplifier la reconstruction de grands segments de code.
L'utilisation du moteur de conceptualisation de type aide désormais à créer les fonctions de fonctionnement suivantes:
<code class="language-bash">npm i typescript --save-dev</code>
La bonne chose est que si vous essayez de faire ProcessBasePay ('ABC'), le type Checker vous avertira. La destruction des contrats de type réduit la prévisibilité des conteneurs d'État. J'utilise un contrat d'exploitation unique comme la paie pour rendre le processeur de paie plus prévisible. Notez que le montant est défini dans l'objet Operation via l'abréviation de l'attribut ES6. L'approche la plus traditionnelle est le montant: le montant, qui est plus verbeux. Les fonctions Arrow, telles que () = & gt; ({}) sont un moyen concis d'écrire des fonctions qui renvoient les littéraux d'objet.
Exemple:
<code class="language-json">"start": "tsc && node .bin/index.js"</code>Le vérificateur de type s'assure que ce sont les valeurs correctes appartenant à cet objet. Avec l'état initial, commencez à créer la fonction de réducteur:
<code class="language-json">{ "compilerOptions": { "strict": true, "lib": ["esnext", "dom"], "outDir": ".bin", "sourceMap": true }, "files": [ "src/index" ] }</code>Le réducteur Redux a un modèle dans lequel tous les types de fonctionnement sont traités par des instructions de commutation. Mais avant d'itérer dans tous les cas de commutateur, je vais créer une variable locale réutilisable:
<code class="language-bash">npm i jest ts-jest @types/jest @types/node --save-dev</code>Notez que si vous ne modifiez pas l'état global, vous pouvez modifier les variables locales. J'utilise l'opérateur LET pour indiquer que cette variable changera à l'avenir. La modification de l'état mondial (comme les paramètres d'état ou opérationnels) peut faire impurer le réducteur. Ce paradigme fonctionnel est crucial car la fonction de réducteur doit être maintenue pure. JavaScript du débutant à Ninja Chapitre 11 (membres de la prime SitePoint uniquement).
Démarrez l'instruction Switch of Reducer pour gérer le premier cas d'utilisation:
<code class="language-json">"jest": { "preset": "ts-jest" }</code>J'utilise l'opérateur ES6 REST pour garder la propriété d'état inchangée. Par exemple, ... état. Vous pouvez écraser n'importe quel attribut après l'opérateur de repos dans un nouvel objet. Basepay vient de la déconstruction, ce qui ressemble beaucoup à la correspondance de motifs dans d'autres langues. La fonction comprotetotalpay est définie comme suit:
<code class="language-typescript">it('is true', () => { expect(true).toBe(true); });</code>Veuillez noter que vous déduquerez les stocks car l'argent sera utilisé pour acheter des actions de l'entreprise. Supposons que vous souhaitiez faire face au remboursement:
<code class="language-bash">npm init</code>
Étant donné que le montant est facultatif, assurez-vous qu'il a une valeur par défaut pour réduire l'échec. C'est l'avantage de TypeScript, car le type vérificateur repére ce piège et vous avertira. Le système de type connaît certains faits, il peut donc faire des hypothèses raisonnables. Supposons que vous souhaitiez faire face au bonus:
<code class="language-bash">npm i typescript --save-dev</code>
Ce mode rend le réducteur lisible car il ne conserve que l'état. Vous obtenez le montant de l'opération, calculez le salaire total et créez un nouveau texte d'objet. Rien n'est différent lorsqu'il s'agit d'options d'achat d'actions:
<code class="language-json">"start": "tsc && node .bin/index.js"</code>
Pour le traitement de la paie le jour de la paie, il nécessite l'effacement des bonus et le remboursement. Ces deux attributs ne sont pas conservés dans l'État dans chaque masse salariale. Et, ajoutez une entrée à l'historique des salaires. Les salaires de base et les options d'achat d'actions peuvent être conservés dans l'État car ils ne changent pas fréquemment. Dans cet esprit, c'est ainsi que Pay_day est géré:
<code class="language-json">{ "compilerOptions": { "strict": true, "lib": ["esnext", "dom"], "outDir": ".bin", "sourceMap": true }, "files": [ "src/index" ] }</code>
Dans un tableau comme NewPayHistory, utilisez l'opérateur d'extension, qui est l'antonym pour le repos. Contrairement au reste de la propriété dans l'objet de collecte, il élargit le projet. Par exemple, [... Payhistory]. Bien que les deux opérateurs ressemblent, ils ne sont pas les mêmes. Regardez attentivement, car cela peut apparaître dans les questions d'entrevue.
Utilisation de POP () pour Payhistory ne changera pas l'état. Pourquoi? Parce que Slice () renvoie un tout nouveau tableau. Les tableaux en JavaScript sont copiés par référence. L'attribution d'un tableau à une nouvelle variable ne modifie pas l'objet sous-jacent. Par conséquent, il faut prendre soin de traiter ces types d'objets.
Parce que LastPayHistory n'est probablement pas défini, j'utilise la fusion de la valeur nulle du pauvre pour l'initialiser à zéro. Veuillez noter que (O && o.property) || 0 Le mode est utilisé pour la fusion. Il peut y avoir un moyen plus élégant de le faire dans les futures versions de JavaScript ou même de type dactylographié.
Chaque réducteur Redux doit définir une branche par défaut. Pour s'assurer que l'État ne devient pas indéfini:
<code class="language-bash">npm i jest ts-jest @types/jest @types/node --save-dev</code>
L'un des nombreux avantages de l'écriture de fonctions pures est qu'elles sont faciles à tester. Les tests unitaires sont un test où vous devez vous attendre à un comportement prévisible, et vous pouvez automatiser tous les tests dans le cadre de la construction. Dans __tests __ / index.test.ts, annulez le test virtuel et importez toutes les fonctions d'intérêt:
<code class="language-json">"jest": { "preset": "ts-jest" }</code>
Notez que toutes les fonctions sont définies pour exporter, vous pouvez donc les importer. Pour le salaire de base, démarrez le réducteur du moteur de paie et testez-le:
<code class="language-typescript">it('is true', () => { expect(true).toBe(true); });</code>
redux définit l'état initial à un non-défini. Par conséquent, c'est toujours une bonne idée de fournir des valeurs par défaut dans la fonction réductrice. Que diriez-vous de gérer le remboursement?
<code class="language-bash">npm i redux --save</code>
Le modèle de bonus de manipulation est le même que celui-ci:
<code class="language-typescript">const BASE_PAY = 'BASE_PAY'; const REIMBURSEMENT = 'REIMBURSEMENT'; const BONUS = 'BONUS'; const STOCK_OPTIONS = 'STOCK_OPTIONS'; const PAY_DAY = 'PAY_DAY';</code>
pour les options d'achat d'actions:
<code class="language-typescript">interface PayrollAction { type: string; amount?: number; }</code>
Notez que lorsque les stocks sont supérieurs à TotalPay, TotalPay doit rester inchangé. Étant donné que cette entreprise hypothétique est éthique, elle ne veut pas prendre d'argent à ses employés. Si vous exécutez ce test, veuillez noter que TotalPay est réglé sur -10, car les stocks seront déduits. C'est pourquoi nous testons le code! Corrigeons l'endroit où le salaire total est calculé:
<code class="language-bash">npm init</code>
Si l'argent gagné par les employés n'a pas assez d'argent pour acheter des actions de l'entreprise, veuillez continuer à ignorer la déduction. Assurez-vous également qu'il réinitialise les stocks à zéro:
<code class="language-bash">npm i typescript --save-dev</code>
Ce correctif détermine s'ils ont suffisamment d'argent dans NewStockoptions. Avec cela, le test unitaire passe, le code est sain et significatif. Nous pouvons tester des cas d'utilisation positifs où il y a suffisamment d'argent pour effectuer des déductions:
<code class="language-json">"start": "tsc && node .bin/index.js"</code>
Pour les jours de paie, utilisez plusieurs statuts pour tester et assurez-vous qu'une transaction unique ne persiste pas:
<code class="language-json">{ "compilerOptions": { "strict": true, "lib": ["esnext", "dom"], "outDir": ".bin", "sourceMap": true }, "files": [ "src/index" ] }</code>
Notez comment j'ajuste OldState pour vérifier le bonus et réinitialiser le remboursement à zéro.
Qu'en est-il de la branche par défaut du réducteur?
<code class="language-bash">npm i jest ts-jest @types/jest @types/node --save-dev</code>
redux définit un type d'opération comme init_action au début. Nous ne nous soucions que si notre réducteur a un ensemble d'état initial.
À ce stade, vous pouvez commencer à vous demander si Redux est plus un modèle de conception. Si vous répondez qu'il s'agit à la fois d'un modèle et d'une bibliothèque légère, vous avez raison. Dans index.ts, importer redux:
<code class="language-json">"jest": { "preset": "ts-jest" }</code>
L'exemple de code suivant peut être enveloppé autour de cette instruction IF. Il s'agit d'un stopgap pour que les tests unitaires ne fuisent pas dans les tests d'intégration:
<code class="language-typescript">it('is true', () => { expect(true).toBe(true); });</code>
Je ne recommande pas de le faire dans des projets réels. Les modules peuvent être placés dans des fichiers séparés pour isoler les composants. Cela facilite la lecture et ne fuit pas de problèmes. Les tests unitaires bénéficient également du fait que les modules fonctionnent indépendamment.
Utilisez PayrollenginerDeducer pour démarrer le stockage redux:
<code class="language-bash">npm i redux --save</code>
Chaque magasin.Subscribe () renvoie une fonction ultérieure de désabscription () qui peut être utilisée pour le nettoyage. Il se désabonne au rappel lorsqu'il est planifié via le stockage. Ici, j'utilise store.getState () pour sortir l'état actuel de la console.
Supposons que l'employé soit gagné 300, avait 50 remboursements, 100 bonus et 15 pour les actions de l'entreprise:
<code class="language-typescript">const BASE_PAY = 'BASE_PAY'; const REIMBURSEMENT = 'REIMBURSEMENT'; const BONUS = 'BONUS'; const STOCK_OPTIONS = 'STOCK_OPTIONS'; const PAY_DAY = 'PAY_DAY';</code>
pour le rendre plus amusant, faire 50 autres remboursements et traiter une autre masse salariale:
<code class="language-typescript">interface PayrollAction { type: string; amount?: number; }</code>
Enfin, exécutez une autre masse salariale et désinscrivez-vous à Redux Storage:
<code class="language-typescript">interface PayStubState { basePay: number; reimbursement: number; bonus: number; stockOptions: number; totalPay: number; payHistory: Array<payhistorystate>; } interface PayHistoryState { totalPay: number; totalCompensation: number; }</payhistorystate></code>
Le résultat final est le suivant:
<code class="language-typescript">export const processBasePay = (amount: number): PayrollAction => ({type: BASE_PAY, amount}); export const processReimbursement = (amount: number): PayrollAction => ({type: REIMBURSEMENT, amount}); export const processBonus = (amount: number): PayrollAction => ({type: BONUS, amount}); export const processStockOptions = (amount: number): PayrollAction => ({type: STOCK_OPTIONS, amount}); export const processPayDay = (): PayrollAction => ({type: PAY_DAY});</code>
Comme indiqué, Redux maintient l'état, modifie l'état et informe les abonnés dans un petit paquet soigné. Considérez Redux comme une machine d'état, qui est la véritable source des données d'état. Tout cela adopte les meilleures pratiques de codage, comme un paradigme fonctionnel solide.
redux fournit une solution simple aux problèmes de gestion des états complexes. Il repose sur le paradigme fonctionnel pour réduire l'imprévisibilité. Parce que le réducteur est une fonction pure, les tests unitaires sont très faciles. J'ai décidé d'utiliser JEST, mais tout cadre de test qui prend en charge les affirmations de base fonctionnera.
TypeScript utilise la théorie du type pour ajouter une couche de protection supplémentaire. Combinez la vérification des types avec la programmation fonctionnelle et vous obtenez un code robuste qui n'est presque jamais interrompu. Plus important encore, TypeScript ne gêne pas le travail tout en ajoutant de la valeur. Si vous remarquez, une fois le contrat de type en place, il n'y a presque pas de codage supplémentaire. Le vérificateur de type fait le reste. Comme tout bon outil, TypeScript automatise la discipline de codage tout en restant invisible. Broking dactylographié bruyamment, mais il mord légèrement.
Si vous souhaitez essayer ce projet (j'espère que vous le faites), vous pouvez trouver le code source de cet article sur GitHub.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!