J'écris actuellement une application d'horaires de bus qui utilise des types d'objets pour modéliser les "documents" d'horaires.
interface Timetable { name: string stops: string[] services: string[][] }
En plus des types, j'ai de nombreuses fonctions, et si je dois utiliser des mutations, je les écris généralement sous forme de méthodes sur la classe. J'utilise principalement Immer donc je n'ai pas besoin d'écrire beaucoup de syntaxe étendue. Par exemple,
const addStop = (timetable: Timetable, stopName: string): Timetable => { return produce(timetable, (newTimetable) => { newTimetable.stops.push(stopName) }) }
Pour gérer l'état, j'utilise Zustand et Immer, mais j'ai l'impression que si j'utilisais Redux, mon problème serait le même. Dans mon magasin, j'ai un tableau d'objets Timetable et une opération qui utilise également Immer pour réaffecter l'objet Timetable actuellement sélectionné :
updateTt: (tt, index) => { set((state) => { state.timetables[index] = tt }) }, updateThisTt: (timetable) => { set((s) => { if (s.selectedTtIdx === null) { throw new Error("no selected timetable") } s.timetables[s.selectedTtIdx] = timetable }) },
Ensuite j'appelle la fonction de changement de données dans le composant React et j'appelle l'opération de mise à jour :
const onAddStop = (name) => { updateThisTt(addStop(timetable, name)) }
Cela fonctionne, mais je ne suis pas sûr de le faire correctement. J'ai maintenant deux couches d'appels Immer, mon composant a désormais des fonctions de modification de données qui sont appelées directement dans ses gestionnaires d'événements, et je n'aime pas vraiment l'apparence des "méthodes" même s'il s'agit globalement d'un bug mineur.
J'ai considéré :
Timetable
类型转换为一个类,将数据修改函数重写为变异方法,并设置 [immerable] = true
et laissez Immer faire tout le travail de mes actions. Je l'ai fait, mais je préfère m'en tenir au mode d'enregistrement immuable. Pour ce que ça vaut, la documentation de Flux, Zustand ou Immer a tendance à afficher la première option, et seulement occasionnellement, aucune application n'est aussi simple que counter = counter + 1
. Quelle est la meilleure façon de créer une application en utilisant l'architecture Flux ?
(Je ne connais pas Zusand et Immer, mais je peux peut-être vous aider...)
Il existe toujours différentes façons et je vous suggère ici ma préférée.
Distinguez clairement les actions de « programmation » et la « mutation » réelle de l'état. (Peut-êtreajoutez un autre niveau entre les deux).
Fonction spécifique « Mutation »
Je recommande de créer des fonctions de "mutation" spécifiques plutôt que génériques, c'est à dire :
updateThisTt:() => { ...
,addStop: () => { ...
.Créez autant de fonctions de mutation que nécessaire, chacune avec un objectif.
Construisez de nouveaux états dans la fonction "mutation"
Conceptuellement, n'utilisez que des générateurs
immer
à l'intérieur des fonctions de mutation.(Je veux dire à propos du magasin. Bien sûr, vous pouvez toujours utiliser
immer
à d'autres fins) .Basé sur cet exemple officiel :
À l'intérieur du composant, vous pouvez maintenant appeler la fonction "mutateur".
Construire un nouveau pays
Si la construction d'un nouvel état devient compliquée, vous pouvez toujours extraire certaines fonctions de "constructeur". Mais considérons d’abord la section suivante, « Fichiers volumineux et duplication ».
Par exemple, votre fonction
addStop
peut également être appelée à l'intérieur d'une mutation Zustand :Fichiers volumineux et doublons
La duplication de code doit certainement être évitée, mais il y a toujours des compromis.
Je ne suggérerai pas d'approche spécifique, mais sachez que le code est souvent plus lu qu'écrit. ParfoisJe pense que cela vaut la peine d'écrire quelques lettres supplémentaires, comme quelque chose comme
state.timetables[index]
plusieurs fois, Si cela rend le but du code plus évident. Vous devez juger par vous-même.Quoi qu'il en soit, je recommande de mettre votre fonction de mutation dans un fichier séparé qui ne fait rien d'autre, De cette façon, cela peut sembler plus facile à comprendre que vous ne le pensez.
Si vous avez un fichier très volumineux mais êtes entièrement concentré uniquement sur la modification de l'état , Et la structure est cohérente, ce qui la rend facile à lire même si vous devez faire défiler Quelques pages.
Par exemple si cela ressemble à ceci :
Notez également que ces fonctions variadiques sont complètement indépendantes (ou le sont-elles ? J'aurais aimé que Zustand utilise des fonctions pures ici, non ?) .
Cela signifie que si cela devient compliqué, vous pouvez même diviser plusieurs fonctions de mutation Divisé en fichiers séparés dans un dossier Comme
/store/mutators/timeTable.js
.Mais vous pouvez facilement le faire à tout moment plus tard.
Créez la "charge utile" ("l'appelant à l'action")
Vous ressentirez peut-être le besoin d'un autre "niveau" Entre gestionnaire d'événements et mutateur. J'ai généralement un calque comme celui-ci, mais je n'ai pas de bon nom pour lui. Appelons-le temporairement « Operation Caller ».
Parfois, il est difficile de décider ce qu'est un « variogramme » et ce qu'est un « variogramme » "Appelant à l'action".
Quoi qu'il en soit, vous pouvez "créer des données" dans "l'appelant d'action", mais vous devriez Aucune manipulation d'État d'aucune sorte n'est effectuée ici , pas même avec
immer
.Transformer l'état et créer une charge utile
C'est une différence subtile et vous ne devriez probablement pas trop vous en soucier (et il peut y avoir des exceptions) , mais à titre d'exemple :
Vous pouvez utiliser certaines parties de l'ancien état dans un "action caller", par exemple :
Mais vous ne devriez pas ne pas mapper (ou transformer) l'ancien état vers le nouvel état, par exemple :
Autre exemple
Aucune suggestion spécifique n'est faite ici, juste un exemple :
Transmettre de nombreuses valeurs aux mutateurs peut prêter à confusion, par exemple :
Dans le gestionnaire d'événements, vous souhaitez vous concentrer sur ce que vous voulez vraiment faire, comme "ajouter un nouvel élément", Mais ne considérez pas tous les arguments. De plus, vous aurez peut-être besoin du nouvel élément pour d’autres choses.
Ou vous pouvez écrire :