0

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`
                    })
                ]
            })
        ])
    })
)
13
  • 1
    You need a proxy for the array itself. Changing the array doesn't do anything to the object that references it.
    – Barmar
    Commented Jan 25 at 20:30
  • 1
    "or changes like array.filter((x, index) => index !== some)": that's not a change.
    – trincot
    Commented Jan 25 at 20:35
  • @trincot I was mean array = array.filter(...)
    – Eugene S
    Commented Jan 25 at 21:33
  • That just assigns a new array to an array variable, and will not change the array being referenced by your (proxied) object.
    – trincot
    Commented Jan 25 at 21:34
  • @Barmar thanks for your reply. Thats the point actually, that im trying to avoid (potentualy) unnecessary proxyfying properties inside of object, even if property is array. I just dont actually get it why i can track array pushes but not filters/deletes. Ofcourse we can afford it to create a proxy in proxy etc to track everything we need in envs like node, but any changes or additional proxies affects the DOM. So thats why this question was asked.
    – Eugene S
    Commented Jan 25 at 21:39

0

Browse other questions tagged or ask your own question.