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
中收到的编号。
所以,我有一个数组,我有 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
中收到的编号。