为什么 Mithril 子组件状态改变不会触发更新?
Why does the Mithril child component state change not trigger an update?
以下代码按预期工作:它创建了两个计数器按钮,它们保持状态并在单击时更新:
let Counter = function (vnode) {
let count = 0
return {
view: (vnode) => {
return m("button",
{
onclick: function () {
console.log(count++)
}
}, "Count: " + count)
}
}
}
let Counters = {
view: () => [
m(Counter),
m(Counter),
]
}
m.mount(document.body, Counters)
但是,如果我在单独的变量中定义 Counter
组件数组并将其传递给 Counters
视图函数,则视图会停止更新。状态持续存在,我可以看到记录到控制台的递增计数,但屏幕上没有任何变化。这是更新后的代码:
let Counter = function (vnode) {
let count = 0
return {
view: (vnode) => {
return m("button",
{
onclick: function () {
console.log(count++)
}
}, "Count: " + count)
}
}
}
let counters =
[
m(Counter),
m(Counter),
]
let Counters = {
view: () => counters
}
m.mount(document.body, Counters)
为什么会这样?这是我正在处理的更复杂的 Mithril 应用程序的玩具示例,我想在其中任意排序子组件数组。
我认为可能发生的情况是当您设置数组时组件正在“渲染”,由于 JavaScript 计算表达式的方式,counters
数组中的 Mithril 组件是在您稍后尝试安装它们之前已经执行,因此 Mithril 重绘机制(通过 m.mount
或 m.route
)可能不会“知道”这些组件,因此不属于重绘队列。
let counters =
[
m(Counter),
m(Counter),
]
我不确定你想要实现什么,但也许更好的解决方案是将数据数组保存在纯 JS 数据结构中,当你需要实际渲染组件时,你可以映射在数据结构上,例如
let Counter = function (vnode) {
return {
view: (vnode) => {
return m("button",
{
onclick: function () {
console.log(this, vnode.attrs.count++);
}
}, "Counter #" + vnode.attrs.id +": " + vnode.attrs.count)
}
}
}
let counterList = [
{ id: 1, name: "Something 1", count: 0 },
{ id: 2, name: "Something 2", count: 0 },
{ id: 3, name: "Something 3", count: 0 },
]
let reorderCountersByCountAscending = () => {
counterList.sort((a, b) => (a.count > b.count) ? 1 : -1)
}
let Counters = {
view: () => {
return m('div',
m('button', { onclick: reorderCountersByCountAscending }, 'Reorder Children'),
counterList
.map((counterListItem) => m(Counter, counterListItem))
)
}
}
m.mount(document.body, Counters);
这不是一个理想的实现,有很多状态突变以非常隐含的方式发生,它可能会在现实世界的应用程序中导致很多混乱,但我希望它描述了这种方法。
这里还有几个示例可供查看:
https://jsfiddle.net/9hu2yd1s/
我能够在 Mithril Gitter 聊天中收集到有用的反馈,并将 post 我在下面学到的东西。
我示例中的计数器未更新的原因是因为它们在 counters
数组中定义一次,随后被 returned。由于每次调用 Counters
的 view()
函数时都会对同一个数组进行 returned,因此 Counters
认为不需要更新,因为它认为元素是 returning 没有改变。即使数组中的 Counter
个元素更新,对数组的引用仍然保持不变。
处理这个问题的一种方法是定义一个工厂函数,每次 Counters
上的 view()
函数是 return 一个新的 Counter
元素排序数组叫。此外,应用程序状态需要保存在全局位置,传递给每个 Counter
的唯一参数是它引用的全局计数器的索引,如下所示:
let counterData = [0, 0]
let Counter = {
view: ({
attrs: {
index
}
}) => {
return m("button", {
onclick: () => counterData[index]++
}, "Count: " + counterData[index])
}
}
let Counters = function () {
const counters = () => {
return counterData.sort((a, b) => a - b).map((count, i) => m(Counter, {
index: i
}, count))
}
return {
view: () => counters()
}
}
m.mount(document.body, Counters)
可以找到一个有效的 JSFiddle here。
以下代码按预期工作:它创建了两个计数器按钮,它们保持状态并在单击时更新:
let Counter = function (vnode) {
let count = 0
return {
view: (vnode) => {
return m("button",
{
onclick: function () {
console.log(count++)
}
}, "Count: " + count)
}
}
}
let Counters = {
view: () => [
m(Counter),
m(Counter),
]
}
m.mount(document.body, Counters)
但是,如果我在单独的变量中定义 Counter
组件数组并将其传递给 Counters
视图函数,则视图会停止更新。状态持续存在,我可以看到记录到控制台的递增计数,但屏幕上没有任何变化。这是更新后的代码:
let Counter = function (vnode) {
let count = 0
return {
view: (vnode) => {
return m("button",
{
onclick: function () {
console.log(count++)
}
}, "Count: " + count)
}
}
}
let counters =
[
m(Counter),
m(Counter),
]
let Counters = {
view: () => counters
}
m.mount(document.body, Counters)
为什么会这样?这是我正在处理的更复杂的 Mithril 应用程序的玩具示例,我想在其中任意排序子组件数组。
我认为可能发生的情况是当您设置数组时组件正在“渲染”,由于 JavaScript 计算表达式的方式,counters
数组中的 Mithril 组件是在您稍后尝试安装它们之前已经执行,因此 Mithril 重绘机制(通过 m.mount
或 m.route
)可能不会“知道”这些组件,因此不属于重绘队列。
let counters =
[
m(Counter),
m(Counter),
]
我不确定你想要实现什么,但也许更好的解决方案是将数据数组保存在纯 JS 数据结构中,当你需要实际渲染组件时,你可以映射在数据结构上,例如
let Counter = function (vnode) {
return {
view: (vnode) => {
return m("button",
{
onclick: function () {
console.log(this, vnode.attrs.count++);
}
}, "Counter #" + vnode.attrs.id +": " + vnode.attrs.count)
}
}
}
let counterList = [
{ id: 1, name: "Something 1", count: 0 },
{ id: 2, name: "Something 2", count: 0 },
{ id: 3, name: "Something 3", count: 0 },
]
let reorderCountersByCountAscending = () => {
counterList.sort((a, b) => (a.count > b.count) ? 1 : -1)
}
let Counters = {
view: () => {
return m('div',
m('button', { onclick: reorderCountersByCountAscending }, 'Reorder Children'),
counterList
.map((counterListItem) => m(Counter, counterListItem))
)
}
}
m.mount(document.body, Counters);
这不是一个理想的实现,有很多状态突变以非常隐含的方式发生,它可能会在现实世界的应用程序中导致很多混乱,但我希望它描述了这种方法。
这里还有几个示例可供查看: https://jsfiddle.net/9hu2yd1s/
我能够在 Mithril Gitter 聊天中收集到有用的反馈,并将 post 我在下面学到的东西。
我示例中的计数器未更新的原因是因为它们在 counters
数组中定义一次,随后被 returned。由于每次调用 Counters
的 view()
函数时都会对同一个数组进行 returned,因此 Counters
认为不需要更新,因为它认为元素是 returning 没有改变。即使数组中的 Counter
个元素更新,对数组的引用仍然保持不变。
处理这个问题的一种方法是定义一个工厂函数,每次 Counters
上的 view()
函数是 return 一个新的 Counter
元素排序数组叫。此外,应用程序状态需要保存在全局位置,传递给每个 Counter
的唯一参数是它引用的全局计数器的索引,如下所示:
let counterData = [0, 0]
let Counter = {
view: ({
attrs: {
index
}
}) => {
return m("button", {
onclick: () => counterData[index]++
}, "Count: " + counterData[index])
}
}
let Counters = function () {
const counters = () => {
return counterData.sort((a, b) => a - b).map((count, i) => m(Counter, {
index: i
}, count))
}
return {
view: () => counters()
}
}
m.mount(document.body, Counters)
可以找到一个有效的 JSFiddle here。