2

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"?

Lask3r
  • 137
  • 1
  • 1
  • 6
  • why does `layout` follow another pattern than `workingLayout`? i guess this may be the problem, or even your `render:` methods - they are also not implemented the same way. I am not familiar with mithril.js but try to do both the same way – messerbill May 07 '19 at 15:00
  • This is just an example show what works and what doesn't. I currently have my components designed in similar to the "/workingLayout" pattern, but am trying to understand why the "layout" function causes a loop when consumed. – Lask3r May 07 '19 at 15:03
  • I believe your issue is connected to the `return { view: function () {} }` you added inside `var layout`. Javascript has explicit return statements, the last statement executed is not automatically returned when the function execution ends. Try removing the `return` statement and let us know what happens. You changed this behavior, which could lead to strange effects. Please point us to the exact page in the documentation where you found this syntax and explain us why you are trying to do it differently. – Fabrizio Bertoglio May 10 '19 at 10:15

2 Answers2

1

From just an initial read: layout returns a function whereas workingLayout appears to simply define a function and then exit (returning nothing). That includes a call to m(...) but if a call to m(...) is calling that returned function, you have a loop.

As a general good strategy for debugging, when you have a working example and a non-working example, make small changes to one that make it more like the other and test, then repeat, until the behavior of the one you are changing switches to have the same behavior as the one you're changing it to be more like. Then toggle that change back and forth and test that the behavior changes back and forth with it, to confirm that this particular change makes the difference. That's part of making a minimal reproducible example and will often help you solve the problem directly.

WBT
  • 2,249
  • 3
  • 28
  • 40
0

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.

Lask3r
  • 137
  • 1
  • 1
  • 6