Detect object changes with JavaScript Proxy

Chris Bongers - Jul 10 '22 - - Dev Community

Let's say we have an object for our users. How can we detect when a property changes?

const user = {
  firstName: 'Chris',
  lastName: 'Bongers',
  age: 10,
};
Enter fullscreen mode Exit fullscreen mode

Now the user changes his age by using the following code.

user.age = 33;
Enter fullscreen mode Exit fullscreen mode

However, we want to log this change to keep track of specific changes.

Using JavaScript Proxy to detect object changes

That's again where the Proxy object comes in handy.
As we learned in the previous article, the Proxy object comes with a set handler (trap).

The set handler can pass us the following three parameters.

  • object: The whole object we are trying to modify
  • property: The property we are trying to modify
  • value: The new value we are trying to set

Let's create a proxy to our user object so we can attach a handler.

const handler = {
  set(target, prop, value) {
    // Our code
  },
};

const proxyUser = new Proxy(user, handler);
Enter fullscreen mode Exit fullscreen mode

As for the code, we want to log which property is being changed, what the previous value was, and what the new value will be.

Then we need to ensure that we set the new value.

The result is the following function.

const handler = {
  set(target, prop, value) {
    console.log(`changed ${prop} from ${target[prop]} to ${value}`);
    target[prop] = value;
  },
};
Enter fullscreen mode Exit fullscreen mode

Let's try it out by modifying the age again.

proxyUser.age = 33;
Enter fullscreen mode Exit fullscreen mode

Now the console will show this change and log changed age from 10 to 33.

Detecting additional Object properties

Sometimes we might push new properties to the object. Let's see how we can capture that.

proxyUser.email = 'info@daily-dev-tips.com';
Enter fullscreen mode Exit fullscreen mode

And again, this will neatly log the change: changed email from undefined to info@daily-dev-tips.com.

However, there is one small exception.

If we have a sub-object or array in our main object, it won't work out of the box.

const user = {
  firstName: 'Chris',
  lastName: 'Bongers',
  age: 10,
  address: {
    postalCode: '1234AB',
  },
};

proxyUser.address.postalCode = '5678CD';
Enter fullscreen mode Exit fullscreen mode

This now logs nothing new, but the property is changed!
And that's because there is now a deep proxy set.

To log on that level, we can again leverage the get handler and proxy each property to be a proxy itself 🤯.

const handler = {
  get(target, key) {
    if (typeof target[key] === 'object' && target[key] !== null) {
      return new Proxy(target[key], handler);
    }
    return target[key];
  },
  set(target, prop, value) {
    console.log(`changed ${prop} from ${target[prop]} to ${value}`);
    target[prop] = value;
  },
};
Enter fullscreen mode Exit fullscreen mode

And now, when we rerun our code, we see the log appear as changed postalCode from 1234AB to 5678CD.

I added these examples to CodePen so you can try them out yourself.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .