Messing around with Proxy objects in Javascript!
Tags: typescript, proxyobject, eventlisteners, stateupdate
Published: 2023-01-04 21:28:32

---
title: Messing around with Proxy objects in Javascript!
published: false
description: This post messes around with the Proxy object in Javascript to make use it of as a sort of store where components can listen for updates to this store to change their own behaviour!
tags: typescript, proxyobject, eventlisteners, stateupdate
cover_image: https://i.imgur.com/plj5Eok.png
published: true
# Use a ratio of 100:42 for best results.
# published_at: 2022-12-23 15:45 +0000
---

## The Problem
Let's say you are working with a few isolated components that have no idea who their children are nor their parents (Because they could be any and many), now each of these components rely on certain information that updates continuously from a (say) library.

So now each component is expected to go on and say 
```js
...
library.addEventListener('someUpdate', doSomething);
...
``` 
But when your number of events and your number of components keep going up, you start approaching __memory leak__ scenarios and just memory issues on browsers on lower tier machines/phones.

## The solution?

What if we build a single store that listens to all of these events so you have only one listener for each type of property! BUT, how does each __isolated__ component actually talk to the store, Another event listener? That was the problem we were trying to solve! 🤦‍♂️

Introducing,
### The Proxy object.
I am not going to go into depth here but you can check out [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy).

Basically, a Proxy defined as,
```js
const target = {
  message1: "hello",
  message2: "everyone",
};

const handler1 = {};

const proxy1 = new Proxy(target, handler1);
```
can `handle` events happening to the `target` and perform certain actions before or after actually doing what the event expected it to do.

In simpler terms, 
1. For a `backend` developer -> It's a middleware
1. For a `OOP` developer -> It's an overridden getter/setter/(Plus a few more)
1. For a `frontend` developer -> It's a Proxy!

### Solving the problem
Now let's actually solve the problem.
Let's say we want two components to listen for resize events on a div. Now instead of each component adding their own individual listeners we are going to create a `store.ts` that defines a proxy like this
```ts
/**
 * Listener Store
 */

let store = {
  windowHeight: 0,
  windowWidth: 0,
};

let storeProxy = new Proxy(store, {
  set(obj, prop, value) {
    /**
     * Can also put validation here
     */
    obj[prop] = value;
    return true;
  },
});
```

Then creates a resize observer which updates the value of the proxy.
```ts
const resizeObserver = new ResizeObserver((entries) => {
  const entry = entries[0];
  const { width, height } = entry.contentRect;
  storeProxy.windowHeight = height;
  storeProxy.windowWidth = width;
});

resizeObserver.observe(document.getElementById('app'));

```

And we will also add a variable called `callOnStoreUpdate` which will be an array of functions to be called whenever a store is updated, hence making the entirety of `store.ts`
```ts
/**
 * Listener Store
 */

/**
 * Holds an array of functions to call when any value
 * in store updates
 */
let toCallOnUpdate = [];

let store = {
  windowHeight: 0,
  windowWidth: 0,
};

let storeProxy = new Proxy(store, {
  set(obj, prop, value) {
    /**
     * Can also put validation here
     */
    if (obj[prop] === value) return true;
    obj[prop] = value;
    toCallOnUpdate.forEach((fun) => fun(obj, prop, value));
    return true;
  },
});

const resizeObserver = new ResizeObserver((entries) => {
  const entry = entries[0];
  const { width, height } = entry.contentRect;
  storeProxy.windowHeight = height;
  storeProxy.windowWidth = width;
});

resizeObserver.observe(document.getElementById('app'));

export const callOnStoreUpdate = (fun: (obj, prop, value) => void) => {
  toCallOnUpdate.push(fun);
};
```

I think now you might have understood where we were getting at.

We simply in how many ever components we want go and add
```ts
/**
 * Sample component!
 */
import { callOnStoreUpdate } from '../listeners';

const onResize = (obj, prop, value) => {
  switch (prop) {
    case 'windowHeight':
      console.log('This is component.ts reporting the window height!', value);
      break;
    case 'windowWidth':
      console.log('This is component.ts reporting the window width!', value);
  }
};

callOnStoreUpdate(onResize);
```

And boom 💥! We have managed to capture updates to a variable without the need to ever add more than one event listener.

I will be exploring a bit more into Proxy and just making sure I understood everything correctly, but I feel like this is an under-utilised web api which can really do crazy things!

This was a problem that we faced over at [Dyte](dyte.io), check us out we're pretty cool.

Also a full working sample. [Stackblitz](https://stackblitz.com/edit/typescript-g6axfh?file=listeners.ts)