Watch for Object Changes with JavaScript

By  on  

Watching for changes to an object's property has always been a much sought after task; many shims have been used over the years to listen to object changes.  These days we have better methods for listening to object changes:  the Proxy API.  Sindre Sorhus has created on-change, a tiny JavaScript tool that allows you to listen for changes on JavaScript objects and arrays!

on-change JavaScript

Since the on-change code is so tiny, here it is in its full glory:

'use strict';

module.exports = (object, onChange) => {
	const handler = {
		get(target, property, receiver) {
			try {
				return new Proxy(target[property], handler);
			} catch (err) {
				return Reflect.get(target, property, receiver);
			}
		},
		defineProperty(target, property, descriptor) {
			onChange();
			return Reflect.defineProperty(target, property, descriptor);
		},
		deleteProperty(target, property) {
			onChange();
			return Reflect.deleteProperty(target, property);
		}
	};

	return new Proxy(object, handler);
};

The onChange function returns a proxy which you'll use to make any object changes.

Using on-change

Pass onChange the object you'd like to spy on and a change handler function:

let j = {
    a: 1
};

// Upon change, save to server
let changeable = onChange(j, () => save(j));

Then use that Proxy to change properties and get notified of changes:


// Make a change that would trigger changes
changeable.a = 2;

// save() is triggered!

// j.a === 2

Note that the original object's values also change -- that's the beauty of Proxy!  Also note that on-change doesn't tell you which property changed; the use case, as Sindre describes, is saving an object (to server, etc.) when any part of an object changes.  You could also use ti for a small library with a render function, re-rendering the content whenever a property changed!

This on-change library is really nice if you don't need to know which property changed, just that something changed.

Recent Features

Incredible Demos

  • By
    CSS Text Overlap

    One of the important functions of CSS is to position elements. Margin, padding, top, left, right, bottom, position, and z-index are just a few of the major players in CSS positioning. By using the above spacing...

  • By
    Highlight Table Rows, Columns, and Cells Using MooTools 1.2.3

    Row highlighting and individual cell highlighting in tables is pretty simple in every browser that supports :hover on all elements (basically everything except IE6). Column highlighting is a bit more difficult. Luckily MooTools 1.2.3 makes the process easy. The XHTML A normal table. The cells...

Discussion

  1. If someone wonders what this Reflect object it. I stumbled across on-change a few days ago and wrote about it. :)

    https://www.stefanjudis.com/today-i-learned/the-global-reflect-object-its-use-cases-and-things-to-watch-out-for/

  2. Dave

    Just a note that watching an object really bogs down all accesses to the object. It’s great for debugging but don’t build a lot of functionality around it in your production code.

  3. Raj

    It is worth mentioning that arrays are not handled by this function and push/pop/delete to an array needs to be handled by overwriting those methods.

  4. Jean-Guy Boulay

    Thanks for this David, I’m relatively novice to programming (used mostly in webobjects within Storyline) so forgive me if my question seems idiotic. I checked out Proxy Api and it’s browser compatibility excludes IE. Is there an alternative to onChange for IE?

  5. javi

    Nice solution,

    But to create a proxy for change notification purposes is not the same that observe changes on an object. In your solution, you create a different object – the proxy – to operate with and force the developer to abandon the use of the original object. The best explanation is an example:

       let j     = { x : 1 }
       let proxy = onChange (j, ...)
    
       proxy.x = 2 // works!
       j.x = 2     // does not work! 
    

    Why this detail is, sometimes, important. Because, sometimes, you are not the owner of the object j and the owner does not want to change the j reference for your proxy. Imagine a situation where you are a framework developer and want to observe changes in some code of your user by maintaining transparency from her perspective.

    If you want another example I can detail the corner case that brought me here. In my case, my j object is a DOM element and I cannot replace it with a proxy.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!