I am building a personal webpage with Mithril JS by following the simple application example in https://mithril.js.org/simple-application.html and the layout example in http://lhorie.github.io/mithril-blog/better-templates-with-fp.html. But I keep running into an infinite loop with a component that needs to load data from a file.
The layout component doesn't loop if I pass an inner file-loading component to it via "vnode.attrs". However, it loops if I build the layout component using the inner component in a function. I'm unable to understand the difference.
Failing example:
hello.json
{"text": "hello"}
index.html
<!DOCTYPE html>
<body>
<script src="https://unpkg.com/mithril/mithril.js"></script>
<script src="index.js"></script>
</body>
index.js
var Hello = {
loadedObj: {},
load: function () {
return m.request({
method: "GET",
url: "hello.json",
}).then(function (result) { Hello.loadedObj = result })
}
}
var HelloBox = {
oninit: Hello.load,
view: function () { return m("div", {}, Hello.loadedObj.text) }
}
var layout = function (comp) {
return {
view: function () { return m('div', {}, m(comp)) }
}
}
var workingLayout = {
view: function (vnode) { return m('div', {}, m(vnode.attrs.comp)) }
}
m.route(document.body, "/workinghello", {
"/hello": {
render: function () {
console.log("rendering /hello")
return m(layout(HelloBox))
}
},
"/workinghello": {
render: function () {
console.log("rendering /workinghello")
return m(workingLayout, { comp: HelloBox })
}
}
})
Here, the route "/workinghello" works, but "/hello" gets into a loop. Why? The "/workinghello" design seems like a code smell to me as "vnode.attrs" is generally used only to pass data to components in the documentation and not components themselves. Is there a way to fix "/hello" or simplify "/workinghello"?
The issue was my misunderstanding of layout components and how m.render
behaves. This precise behavior is addressed in the "Wrapping a Layout Component" section of the [documentation](https://mithril.js.org/route.html#advanced-component-resolution}. The response from Hello.load
triggers a redraw, which calls renders the /hello
route again, causing an infinite loop. I was able to clean up the routing with the following design (as suggested in the doc link). If the layout component is defined as:
var fixedLayout = {
view: function(vnode) {
return vnode.children
}
}
and the router uses the component like so:
"/fixedhello": {
render: function() {
return m(fixedLayout, m(HelloBox))
}
}
there is no loop.