Est-ce que votre Elfe est malade ? Il est dans quel état ? Ici on ne traite pas de ses caractéristiques, mais bien de son état général.
Maintenant que vous avez vu à quoi ressemblent les propriétés, qui sont
l’équivalent des setX
Goblins, voyons comment faire pour donner un état
(state
) à un Elfe. Pour rappel, le state
doit contenir uniquement des
valeurs sérialisables (il peut y avoir des exceptions non documentées ici). Les
différences entre les propriétés et les states
ne se limitent pas au
protected
/ public
mais à ce qu’on désire faire avec le service. Le state
permet de gérer les mutations dans une fonction pure (sans effet de bord) que
l’on nomme reducer
. Il est possible ainsi de réaliser des tests efficacement
sur les states
sans faire intervenir les effets de bords qui doivent être
produits uniquement par les quêtes. De plus, les states
sont immuables
(immutables) par nature, ce qui donne de nombreux avantages pour le
fonctionnement du framework. Néanmoins, concernant les Elfes, les states
sont
mutables (du point de vue du consomateur) contrairement aux Goblins.
Les states
doivent également être utilisés pour la persistance des données
contrairement aux propriétés.
Bien qu’on parle toujours de reducer, les Elfes n’utilisent pas le même prototype qu’un reducer habituel. Voici celui que vous connaissez bien :
(state, action) => state.set();
Avec les Elfes, l’action est décomposée en arguments et le
state
est injecté dans lethis
pour devenirthis.state
:
(val1, val2, ..., valN) => this.state.valN = valN;
Dans l’exemple ci-dessus vous pouvez voir this.state.valN = valN
. Connaissant
bien les Goblins, vous devriez être surpris. Il n’y a plus besoin d’effectuer un
.set()
pour assigner une valeur. De même avec la lecture, le .get()
n’existe
plus.
Bien entendu, en dehors du reducer, le state
est toujours immutable.
Un Elfe est un être typé. Il connait son état dans ses moindres détails.
const {string, number} = require('xcraft-core-stones');
class ElrondShape {
id = string;
name = string;
yearsOfLife = number;
}
class ElrondState extends Elf.Sculpt(ElrondShape) {}
L’exemple ci-dessus est un shape. Ce shape permet de décrire précisément à
quoi doit ressembler un state. Une fonction elfique particulière,
Elf.Sculpt()
, permet de sculpter un state elfique à partir d’un shape.
Pour bien comprendre les termes, voyez la classe shape comme une définition, et la classe state comme un type. C’est ce type qui va permettre d’instancier un state à notre Elfe.
Quand un Elfe réfléchit, il se fie à sa mémoire structurée et bien définie. Cette mémoire est une instance du type décrit précédemment.
class ElrondLogic extends Elf.Spirit {
/* Instance du state Elrond avec des valeurs initiales */
state = new ElrondState({
name: 'Elrond',
yearsOfLife: 0,
});
/* Reducer pour la quête create */
create(id, desktopId) {
this.state.id = id;
}
/* Reducer pour la quête nextYear */
nextYear() {
this.state.yearsOfLife++;
}
}
La logique de l’Efle doit dériver de l’esprit Elfe Elf.Spirit
. En effet, tous
les Elfes partagent les mêmes fondamentaux.
Nous sommes ici dans la définitions des reducers et de l’état initial. La
propriété state
est réservée à ce but et doit toujours se nommer ainsi. Le
this
des reducers est un peu particulier. Comprennez bien que le this
ne
représente pas le this
de la classe Elfe. Les reducers n’ont pas vraiment
changé par rapport aux Goblins. Vous êtes ici dans le résultat d’un dispatch
Redux où tout est pure. C’est depuis ce this
que vous pouvez récupérer (si
vous le souhaitez) le state immutable via this.immutable
.
La classe de l’Elfe …
class Elrond extends Elf {
state = new ElrondState();
/* La quête (constructeur) */
async create(id, desktopId = null) {
this.do();
return this;
}
/* La quête (effets de bord) */
async nextYear() {
this.do();
return this.state.yearsOfLife;
}
}
L’Elfe décrit ses quêtes dans sa classe, en plus des propriétés. Les reducers sont séparés volontairement et peuvent alors être testés indépendamment de l’Elfe.
Pour atteindre l’état de l’Elfe, il suffit d’utiliser la propriété this.state
.
Notez bien que pour y avoir accès, vous devez la déclarer comme propriété de
l’Elfe.
Peut-être que vous trouvez cela étrange car vous avez aussi déclaré une propriété
state
dans la classeElrondLogic
. Je vous propose qu’on garde un peu de mystère. Faites comme expliqué dans cette documentation et tout ira bien. N’utilisez pas l’instance destate
dans l’Elfe pour y passer les valeurs initiales (ça ne fonctionnera pas).
Comme vous le savez bien avec les Goblins, tous les paramètres donnés à une
quête sont forcément disponibles dans l’action
passé au reducer. Pour ce qui
est des Elfes, c’est exactement la même chose, et comme avec les Goblins, il est
possible d’en donner d’autres.
L’exemple ci-dessous permet de montrer que l’on peut récupérer directement
l’id
depuis le reducer, mais également l’age
qui ne fait pas partie du
prototype de la quête create
. Ce n’est pas un problème car l’age
est donné
explicitement avec l’appel sur le do()
.
class ElrondLogic extends Elf.Spirit {
/* ... */
create(id, age) {
this.state.id = id;
this.state.age = age;
}
}
class Elrond extends Elf {
/* ... */
async create(id, desktopId = null) {
this.do({age: 143});
return this;
}
}
Si on avait aussi spécifié
desktopId
dans le prototype de la méthodecreate
de la logique, on aurait également pu le récupérer directement sans le spécifier avec l’appelthis.do()
.
C’est à la naissance de l’Elfe que sa logique est construite. Les bébé Elfes ne sont pas comme les autres.
const {Elf} = require('xcraft-core-goblin');
const Elrond = require('./lib/elrond/service.js');
const ElrondLogic = require('./lib/elrond/logic.js');
exports.xcraftCommands = Elf.birth(Elrond, ElrondLogic);
Voir aussi : La naissance