Trap array changes (add/delete) inside object of proxy

78 Views Asked by At

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`
                    })
                ]
            })
        ])
    })
)
0

There are 0 best solutions below