I am trying to track some array changes inside of proxyfied object. For example:
object = {
count: 0,
input: ``,
tasks: [ ]
}
and I need to track changes of object.tasks
.
The problem is that proxy catches all changes (set, get etc.) related to object, and even array (object.tasks
) sets (means event 'set' fires and traps array changes, but not removes (delete) like delete object.tasks[someIndex]
, or changes like array.filter((x, index) => index !== some)
and so on.
So my question is: what's the proper way to trap this changes?
upd:
const createElement = (any) => {
const { tagName = undefined, className = '', id = undefined, state = { }, render = undefined, ...some } = any
const element = tagName ? document.createElement(tagName) : document.createDocumentFragment()
if(tagName){
if(className) element.className = className
if(id) element.id = id
}
if(render){
if(state){
element.state = new Proxy(state, {
deleteProperty(entry, v){
console.log(entry, v)
delete(entry[v])
return true
},
set(entry, k, v){
if(entry[k] !== v){
console.log(entry, v)
entry[k] = v
const children = render({ state: entry })
element.childNodes.forEach((child, i) => {
if(typeof(children[i]) === 'string' || typeof(children[i]) === 'number') children[i] = document.createTextNode(children[i])
!children[i].isEqualNode(child) && element.replaceChild(children[i], child) && console.log('Element updated: ', children[i])
})
} else {
console.log(k, v)
}
return true
}
})
}
const children = render({ state: element.state })
if(typeof(children) === 'object' && children instanceof(Array)) element.replaceChildren(...children)
else element.replaceChildren(children)
}
if(some){
Object.assign(element, some)
}
return element
}
document.querySelector('#root').replaceChildren(
createElement({ tagName: 'div', className: 'class-test',
render: () => ([
`buttons test (dec/inc)`,
createElement({ tagName: 'div', state: { count: 0 },
render: ({ state }) => [
createElement({ tagName: 'button', className: 'btn-decrease',
onclick: () => state.count--,
render: () => `-`
}),
`Clicked ${ state.count } times...`,
createElement({ tagName: 'button', className: 'btn-increase',
onclick: () => state.count++,
render: () => `+`
})
]
}),
`inputs test (tasks, click on task to remove)`,
createElement({ tagName: 'form', state: { input: ``, tasks: [ ] },
onsubmit: (e) => {
e.preventDefault()
e.target.reset()
},
render: ({ state }) => [
createElement({ tagName: `ul`,
render: () => state.tasks.map((task, i) => createElement({
tagName: 'li',
onclick: () => delete state.tasks[i],
render: () => task
}))
}),
createElement({ tagName: 'input', placeholder: `Text some task..`,
oninput: ({ target }) => state.input = target.value
}),
createElement({ tagName: 'button', type: 'submit',
onclick: () => state.input ? state.tasks.push(state.input) && (state.input = ``) : null,
render: () => `Set task`
})
]
})
])
})
)
array
variable, and will not change the array being referenced by your (proxied) object.