Last active
December 11, 2020 07:35
-
-
Save createvibe/f6806d5df97d848170bbe6486db24726 to your computer and use it in GitHub Desktop.
Deep Observable Proxy - Maintains the observed object path for complex data types, so developers can know which root properties have been modified.
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
/** | |
* The ProxyObserver recursively wraps complex objects for observations | |
* @param {{}|[]} target The target you want to observer | |
* @param {function} observer | |
* @throws TypeError if observer is not a function | |
*/ | |
function ProxyObserver(target, observer) { | |
if (typeof observer !== 'function') { | |
throw new TypeError('Expecting observer to be a callable function.'); | |
} | |
return new Proxy(target, { | |
get: (target, prop, receiver) => { | |
const value = target[prop]; | |
if (typeof value === 'object') { | |
return ProxyObserver(value, observer.bind(null, { | |
target: target, | |
prop: prop, | |
value: value, | |
oldValue: value, | |
receiver: receiver | |
})); | |
} | |
return value; | |
}, | |
set: (target, prop, value, receiver) => { | |
const oldValue = Reflect.get(target, prop, receiver); | |
if (oldValue === value) { | |
return true; | |
} | |
observer({target, prop, value, oldValue, receiver}); | |
return Reflect.set(target, prop, value, receiver); | |
} | |
}); | |
} |
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
import ProxyObserver from './ProxyObserver'; | |
const data = { | |
one: 'foo', | |
two: [1,'two',3,'four',5,[6,7]], | |
three: { | |
foo: 'test', | |
bar: 'test' | |
} | |
}; | |
const proxy = ProxyObserver(data, function() { | |
console.log('we got a changed value!'); | |
/* | |
the arguments to the function are grouped objects from | |
each level in the object path. | |
we refer to these grouped objects as the observable chain. | |
each link in the chain represents a different observed | |
level in the object path. | |
object.child.data = 'value'; | |
object is level 1 | |
child is level 2 | |
data is level 3 | |
this observable function is called only once per change, | |
at the last level, level 3. the arguments contain an | |
object for each level in the observed path. | |
*/ | |
let name, value; | |
const path = []; | |
const chain = Array.prototype.slice.call(arguments); | |
while (chain.length !== 0) { | |
const link = chain.shift(); | |
path.push( link.prop ); | |
if (!name) { | |
name = link.prop; | |
} | |
value = link.value; | |
} | |
console.log('>', name, '<', path.join('.'), '=', value); | |
// name holds the root property in the proxy that was modified | |
// value holds the new value for the last property in the chain | |
}); | |
proxy.two[5][1] = 'XXX'; | |
// console: we got a changed value! | |
// console: > two < two.5.1 = XXX | |
proxy.three.foo = 'testing nested object'; | |
// console: we got a changed value! | |
// console: > three < three.foo = testing nested object | |
proxy.three.bar = { | |
test: 'new object', | |
list: [1,2,3] | |
}; | |
// console: we got a changed value! | |
// console: > three < three.bar = {test: "new object", list: Array(3)} | |
proxy.three.bar.list.push(4); | |
// console: we got a changed value! | |
// console: > three < three.bar.list.3 = 4 | |
proxy.three.bar.list.splice(2,0,9); | |
// console: we got a changed value! | |
// console: > three < three.bar.list.4 = 4 | |
// console: we got a changed value! | |
// console: > three < three.bar.list.3 = 3 | |
// console: we got a changed value! | |
// console: > three < three.bar.list.2 = 9 | |
console.log(proxy.three.bar.list.slice()); | |
// console: (5) [1, 2, 9, 3, 4] | |
console.log(data.three.bar.list); | |
// console: (5) [1, 2, 9, 3, 4] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment