Yesterday, we looked at vanilla JS Proxies.
Today, I wanted to show you how you can use them to create reactive signals. Let's dig in!
An example
Let's imagine you have a cart
object ({}
).
Whenever it's updated, you have code in multiple different places that needs to know it's been changed, know what changed, and run some actions in response.
// When this happens, some other code should automatically run in response cart.shirt = { size: 'medium', quantity: 1 };
Proxies are perfect for this!
This is actually how libraries like Vue work under-the-hood. If you want to learn more about that, I do a deep-dive into state-based UI over at the Lean Web Club.
Creating a signal()
function using Proxies
First, let's create a signal()
function that accepts a data
value to create a Proxy from.
We'll use a plain object ({}
) as the default value if none is provided. We also want each signal object to have a unique name or identifier. We'll pass in a name
variable for that.
function signal (data = {}, name = '') { // ... }
Next, let's create a handler()
function that returns our handler object. We'll do this to handle nested arrays and objects.
function signal (data = {}, name = '') { /** * Create a Proxy handler object * @param {Object} data The data object * @param {String} name The signal name * @return {Object} The handler object */ function handler (data, name) { return { get (obj, prop) { if (key === '_isProxy') return true; let nested = ['[object Object]', '[object Array]']; let type = Object.prototype.toString.call(obj[key]); if (nested.includes(type) && !obj[key]._isProxy) { obj[prop] = new Proxy(obj[prop], handler(name, data)); } return obj[prop]; }, set (obj, prop, value) { if (obj[prop] === value) return true; obj[prop] = value; return true; }, deleteProperty (obj, prop) { delete obj[prop]; return true; } }; } }
Now, we can create and return
a new Proxy()
.
function signal (data = {}, name = '') { // ... // Create a new Proxy return new Proxy(data, handler(data, name)); }
Emitting a custom event
Let's add an emit()
function to our signal()
function that emits a custom event.
Quick aside: you can find this and lots of other helper functions like it over at the Lean Web Club.
We'll pass in the signal name
, as well as a detail
object with details about what changed.
/** * Emit a custom event * @param {String} name The unique name for the signal * @param {*} detail Any details to pass along with the event */ function emit (name, detail = {}) { // Create a new event let event = new CustomEvent(`signal:${name}`, { bubbles: true, detail: detail }); // Dispatch the event return document.dispatchEvent(event); }
Now, in our handler()
, we can emit()
events when data is set or deleted.
For details, we'll include the prop
that was changed, it's value
, and an action
indicating how it changed.
/** * Create a Proxy handler object * @param {Object} data The data object * @param {String} name The signal name * @return {Object} The handler object */ function handler (data, name) { return { get (obj, prop) { if (key === '_isProxy') return true; let nested = ['[object Object]', '[object Array]']; let type = Object.prototype.toString.call(obj[key]); if (nested.includes(type) && !obj[key]._isProxy) { obj[prop] = new Proxy(obj[prop], handler(name, data)); } return obj[prop]; }, set (obj, prop, value) { if (obj[prop] === value) return true; obj[prop] = value; emit(name, {prop, value, action: 'set'}); return true; }, deleteProperty (obj, prop) { delete obj[prop]; emit(name, {prop, value: obj[prop], action: 'delete'}); return true; } }; }
Using a signal()
Now, we can create a cart
object as a signal()
, like this…
let cart = signal({}, 'cart');
We can listen for changes it to it like this…
document.addEventListener('signal:cart', function (event) { console.log(event.detail); });
And whenever we update our cart
, an event will fire off.
cart.shirt = { size: 'medium', quantity: 1 }; cart.pants = { size: 32, quantity: 2 }; delete cart.pants;
Here's a demo.
Join the Lean Web Club! Coaching. Courses. Coding resources. Get the skills, confidence, and support you need to learn front-end web development and achieve long-term success.
Cheers,
Chris
Want to share this with others or read it later? View it in a browser.
0 Komentar untuk "[Go Make Things] Creating a vanilla JavaScript signal() with Proxies"