Skip to content

Instantly share code, notes, and snippets.

@audinue
Created November 8, 2020 05:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save audinue/819d68c1410049b4615280889caa82ed to your computer and use it in GitHub Desktop.
Save audinue/819d68c1410049b4615280889caa82ed to your computer and use it in GitHub Desktop.
Modify deeply nested immutable object easily.
let foo = {
bar: {
baz: 2
}
}
let foo2 = modify(foo, () => {})
console.assert(foo === foo2)
let foo3 = modify(foo, foo => {
foo.qux = 2
})
console.assert(foo !== foo3)
console.assert(foo.bar === foo3.bar)
let foo4 = modify(foo, foo => {
foo.bar.baz = 2
})
console.assert(foo !== foo4)
// Supports plain objects and arrays only.
const modify = (() => {
let target = Symbol()
let handler = {
get (wrapper, key) {
if (key === target) {
return wrapper.target
}
let value = wrapper.target[key]
if (typeof value === 'object' && value !== null) {
return wrap(value, wrapper, key)
}
return value
},
set (wrapper, key, value) {
if (!wrapper.modified) {
if (Array.isArray(wrapper.target)) {
wrapper.target = wrapper.target.slice(0)
} else {
wrapper.target = { ...wrapper.target }
}
if (wrapper.parent) {
handler.set(wrapper.parent, wrapper.key, wrapper.target)
}
wrapper.modified = true
}
wrapper.target[key] = value
return true
}
}
let wrap = (target, parent, key) =>
new Proxy({
target,
parent,
key,
modified: false
}, handler)
return (state, update) => {
let wrapper = wrap(state)
update(wrapper)
return wrapper[target]
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment