Last active
August 2, 2019 12:16
-
-
Save rudionrails/11ac87de66e12cbd5f38129b7b54734a to your computer and use it in GitHub Desktop.
Track object changes with a JavaScript proxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const pick = (keys, object) => | |
Object.entries(object).reduce( | |
(acc, [key, value]) => (keys.includes(key) ? {...acc, [key]: value} : acc), | |
{}, | |
); | |
const parse = x => JSON.parse(JSON.stringify(x)); | |
export default function proxy(initialAttrs = {}) { | |
let current = parse(initialAttrs); | |
let original = parse(initialAttrs); | |
const utils = { | |
isChanged: () => | |
[...Object.keys(current), Object.keys(original)] | |
.filter((value, index, self) => self.indexOf(value) === index) | |
.some(key => current[key] !== original[key]), | |
applyChanged: newOriginal => | |
Object.assign(original, parse(pick(Object.keys(original), newOriginal))), | |
}; | |
return new Proxy(current, { | |
get: (obj, prop) => utils[prop] || obj[prop], | |
set: (obj, prop, value) => prop in obj && (obj[prop] = value), | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const attrs = {foo: 'bar', bar: 'baz'}; | |
let subject; | |
beforeEach(() => { | |
subject = proxy(attrs); | |
}); | |
test('to return Object', () => { | |
expect(subject.constructor).toEqual(Object); | |
}); | |
test('to #get value for existing key', () => { | |
expect(subject.foo).toEqual('bar'); | |
expect(subject.baz).toBe(undefined); | |
}); | |
test('to #set value for existing key', () => { | |
subject.foo = 'baz'; | |
expect(subject.foo).toEqual('baz'); | |
}); | |
test('to not #set value for missing key', () => { | |
subject.baz = 'foo'; | |
expect(subject.baz).toBe(undefined); | |
}); | |
test('to track changed state', () => { | |
expect(subject.isChanged()).toBe(false); | |
subject.foo = 'bar'; | |
expect(subject.isChanged()).toBe(false); | |
subject.foo = 'baz'; | |
expect(subject.isChanged()).toBe(true); | |
}); | |
test('to applyChanged for original state', () => { | |
subject.applyChanged({foo: 'bar'}); | |
expect(subject.foo).toEqual('bar'); | |
expect(subject.isChanged()).toBe(false); | |
subject.applyChanged({foo: 'xyz'}); | |
expect(subject.foo).toEqual('bar'); | |
expect(subject.isChanged()).toBe(true); | |
}); | |
test('to not applyChanged for newly added keys', () => { | |
subject.applyChanged({baz: 'foo'}); | |
expect(subject.foo).toEqual('bar'); | |
expect(subject.bar).toBe('baz'); | |
expect(subject.baz).toBe(undefined); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment