Vous êtes-vous déjà demandé comment fonctionnaient les librairies JavaScript qui mettent à jour la page quand vous modifiez une variable directement sans utiliser un "setter" ? Elles utilisent probablement un Proxy JavaScript !
Dans cet article, nous allons d'abord expliquer ce qu'est un proxy JavaScript, puis nous allons créer un exemple de page réactive grâce à un proxy.
Qu'est-ce qu'un proxy en JavaScript ?
ECMAScript 2015 (ES6) a introduit un nouveau concept en JavaScript : les proxies.
Ces proxies servent à wrapper un objet JavaScript afin d'intercepter les opérations fondamentales qu'on pourrait appliquer sur l'objet. Ces opérations sont, par exemple, l'accès et la modification des propriétés d'un objet.
C'est un peu flou ? Tout va s'éclairer avec des exemples :
const proxy = new Proxy(target, handlers);
Où target est l'objet à wrapper et handlers sont les opérations fondamentales dont nous avons parlé plus haut.
Maintenant, essayons l'opération get, la fonction prend en paramètre 2 choses :
const proxy = new Proxy({age: 21}, { get(target, property) { console.log(Lecture de target.${property}); return target[property]; } }); console.log(proxy.age)
Affichera :
Lecture de target.age 21
Ensuite, l'opération set, elle prend en paramètre 3 choses :
const proxy = new Proxy({age: 21}, { set(target, property, value) { target[property] = value + 1; } }); proxy.age = 41; console.log(proxy.age);
Affichera :
42
Création d'une page réactive grâce à un proxy
Nous allons faire une page qui affiche :
Nous allons faire en sorte que cette page se mette à jour dès que l'on change la variable "age".
Tout d'abord, on fait une page HTML basique et dans le body, on met :
<body> <p>Votre age :</p> <input id="ageInput" type="text"> <h2 id="yourAge"></h2> <h2 id="plusTenYears"></h2> <h2 id="plusTwentyYears"></h2> <h2 id="plusThirtyYears"></h2> </body>
Ensuite, on wrappe l'état de notre page dans dans un proxy :
window.ReactiveSystem = { state: new Proxy({ age: 0 }, {}) }
Puis, on ajoute le système qui permettra de lancer une fonction à chaque fois que l'on modifie une propriété de l'objet.
Pour cela, j'ai choisi d'avoir une Map qui lie le nom de la propriété avec la fonction qu'il faut appeler lorsque la propriété change :
window.ReactiveSystem.onChangeHandler = new Map();
On ajoute une fonction pour associer facilement une propriété avec une fonction :
window.ReactiveSystem.addOnChangeHandler = (property, handler) => { window.ReactiveSystem.onChangeHandler.set(property, handler); }
On ajoute une fonction pour lancer facilement la fonction "onChange" associée à une propriété :
window.ReactiveSystem.runOnChangeHandler = (property) => { const handler = window.ReactiveSystem.onChangeHandler.get(property); handler?.(window.ReactiveSystem.state[property]); }
Ensuite, on ajoute une fonction pour ajouter une nouvelle propriété réactive sur notre page :
window.ReactiveSystem.addState = (property, state, onChangeHandler) => { window.ReactiveSystem.state[property] = state; window.ReactiveSystem.addOnChangeHandler(property, onChangeHandler); window.ReactiveSystem.runOnChangeHandler(name); }
Enfin, on change le set de notre proxy pour qu'il appelle notre fonction runOnChangeHandler
window.ReactiveSystem.state = newProxy({}, { set(target,propertyName,value) { target[propertyName] = value; window.ReactiveSystem.runOnChangeHandler(propertyName); }, });
Maintenant que notre système est fini, il ne manque plus qu'à l'utiliser, pour cela, on crée une fonction qui va mettre à jour les choses liées à l'âge que l'utilisateur rentrera :
const renderAgeThings = (value) => { const rawAge = +value; const age = isNaN(rawAge) ? 0 : rawAge; const agePlusTen = Dans 10 ans vous aurez ${age+10} ans; const agePlusTwenty = Dans 20 ans vous aurez ${age+20} ans; const agePlusThirty = Dans 30 ans vous aurez ${age+30} ans; document.getElementById('yourAge').innerHTML = Vous avez ${age} ans; document.getElementById('plusTenYears').innerHTML = agePlusTen; document.getElementById('plusTwentyYears').innerHTML = agePlusTwenty; document.getElementById('plusThirtyYears').innerHTML = agePlusThirty; document.getElementById('ageInput').value = age.toString(); }
Enfin, il faut utiliser notre fonction addState :
window.ReactiveSystem.addState('age', 21, renderAgeThings);
Si on veut que l'âge change lorsque l'utilisateur modifie son âge avec le text input, il faudra rajouter :
document.getElementById('ageInput').addEventListener('input', (e) => { window.ReactiveSystem.state.age = e.target.value })
C'est fini ! Maintenant, si on ouvre la console JavaScript dans les outils de développement et que l'on modifie la propriété window.ReactiveSystem.age, le contenu de la page est mis à jour !