The following code works as expected: it creates two counter buttons that persist their state and update on clicking:
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)
However, if I define the array of Counter
components in a separate variable and pass that to the Counters
view function, then the view stops updating. The state persists and I can see incrementing count logged to console, but nothing changes on screen. This is the updated code:
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)
Why would this be happening? This is a toy example of a more complicated Mithril application that I'm working on, where I would like to arbitrarily sort the array of child components.
I was able to gather useful feedback in the Mithril Gitter chat and will post what I learned below.
The reason the counters in my example were not updating was because they were defined once inside the counters
array and it was returned subsequently. As the same array is returned every time the view()
function of Counters
is called, Counters
does not see a need to update as it thinks that the elements it is returning have not changed. Even though the Counter
elements inside the array update, the reference to the array remains the same.
One way of handling this is by defining a factory function that would return a new sorted array of Counter
elements each time the view()
function on Counters
is called. Furthermore, the application state then needs to be kept in a global location and the only parameter passed to each Counter
is the index of the global counter it is referencing, like so:
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)
A working JSFiddle can be found here.