Mithril 组件状态使用 unshift 呈现不同

Mithril component state renders differently with unshift

所以,我有一个数组,我有 2 个组件(子组件和父组件),我在父组件中遍历数组,我渲染子组件,我从数组中给它们 attrs(props)。

子组件有它们的 attrs(props) 递增和递减。

父组件可以将新项添加到数组中并重新渲染。

问题:为什么 chid 使用 .push() 可以正常显示,而使用 .unshift() 则不能正常显示。此外,concat 和 [newItem, ...oldArray] 一切正常,但是当他们在数组前面添加项目时,同样的事情不好吗?还有如何正确地 .unshift() 新项目(评论、计数器、图像、帖子,例如任何东西)进入状态,以便它们首先呈现?

PS:React、Infero 和 Aurelia 也会出现这种情况。

PPS: 我是比较反应的人,也许我对待 Mithril 的 vnode 不是我想的那样。

const root = document.getElementById('root')

var data = [0, 12, -10, 1, 0]

class app {
    oninit(vnode){
        vnode.state.data = data
    }
    oncreate(vnode) {
        vnode.state.addCounter = function(e){
            vnode.state.data.unshift(1)
        }
    }
    view(vnode){
        if(vnode.state.data){
            return([
                m('button',{onclick: vnode.state.addCounter},'add another counter'),
                vnode.state.data.map((e,i)=>{
                    return(
                        m(Counter, {data: e, key: i})
                    )
                })
            ])
        }
    }
}

class Counter {
    oninit(vnode){
        vnode.state.data = vnode.attrs.data
        vnode.state.increment = function(e){
            vnode.state.data = vnode.state.data + 1
        }
        vnode.state.decrement = function(e){
            vnode.state.data = vnode.state.data - 1;
        }
    }
    view(vnode){
        return(
            m('p',{style: 'display:inline;margin: 10px;'},[
                m('b',{onclick: vnode.state.increment, style:'cursor: pointer'}, '+'),
                m('i', vnode.state.data),
                m('b',{onclick: vnode.state.decrement, style:'cursor: pointer'}, '-')
            ])
        )
    }
}

m.mount(root, app)

错误是由key引起的。渲染 children:

时使用数组索引作为键
vnode.state.data.map((e,i)=>{
    return(
        m(Counter, {data: e, key: i})
    )
})

简单的解决方案是使用正确的密钥,在这种情况下唯一有意义的是传递给 child 的数字:

vnode.state.data.map((number)=>{
    return(
        m(Counter, {data: number, key: number})
    )
})

Fiddle: https://jsfiddle.net/6kh0ggrb/


你的代码失败的地方

通常使用数组索引作为键是一个坏主意(在任何键控 library/framework 中,包括 Mithril.js),而您的 use-case 就是一个很好的例子。

让我们来看看发生了什么step-by-step:

1:设置

您从一个数组 [0, 12, -10, 1, 0] 开始,然后为数组中的各个数字渲染出 Counter。新组件具有键 0 到 4。

------------------------
| key | number | shows |
------------------------
|   0 |      0 |     0 |
|   1 |     12 |    12 |
|   2 |    -10 |   -10 |
|   3 |      1 |     1 |
|   4 |      0 |     0 |
------------------------

2: 添加到数组的开头

你将 1 添加到数组的开头,得到 [1, 0, 12, -10, 1, 0]。正如您所期望的那样工作。然而,当你渲染它时,前 5 children 有一个秘银重新识别的密钥,而不是创建新的组件,秘银使用与以前相同的组件。因此,在您的情况下,前五个组件被重用。

因为您的每个 Counter`s 只关心 attrs 它收到 :

class Counter {
    oninit(vnode){
        vnode.state.data = vnode.attrs.data
        ...

...重复使用相同的组件来呈现新数字将不起作用

------------------------------------------------
| key | current number |   prev number | shows |
------------------------------------------------
|   0 |              1 |             0 |     0 |
|   1 |              0 |            12 |    12 |
|   2 |             12 |           -10 |   -10 |
|   3 |            -10 |             1 |     1 |
|   4 |              1 |             0 |     0 |
|   5 |              0 | new component |     0 |
------------------------------------------------

现有组件重复使用它们初始化时使用的编号,只有最后一个使用它在 attrs 中收到的编号。