Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple apps in same page cause multiple redraws of the first mounted app #674

Closed
altunyurt opened this issue Jun 17, 2015 · 10 comments
Closed

Comments

@altunyurt
Copy link

A simple demonstration is at https://jsfiddle.net/t42co8ks/2/

Simply, binding the other modules to page causes the first module to redraw, even if they are not even related.

Is this a known behavior? How can i prevent the first module from redrawing?

@lhorie
Copy link
Member

lhorie commented Jun 17, 2015

Yes, it's known behavior. Multiple tenancy is currently not supported

@elektronik2k5
Copy link

I actually rely exactly on this behavior. @altunyurt, why do you care about redraws when you've got a virtual DOM?

@altunyurt
Copy link
Author

@elektronik2k5 I'm firing up some requests when the elements are created, and in this case, multiple requests are made, for the element is created multiple times.

How do you rely on this behavior?

@lhorie does this behavior only cause firing the first component twice, or may any other unexpected things happen too?

@barneycarroll
Copy link
Member

@altunyurt there are 2 issues here:

  1. Mithril redraws are global, so there is no way to tell only a specific component to redraw.
  2. When you mount several components directly into the DOM immediately, there will be 2 draw loops.

Why is issue 2 occurring? As Leo says, Mithril redraws are global, so whenever Mithril redraws, all views are re-computed. Another feature of redraws is that normally they are 'batched', or 'queued', meaning that if many redraws are triggered one after the other within a very short time, they will result in only a single redraw. There are some exceptions though: the very first time Mithril draws, it happens immediately. So in your situation what happens is this:

  1. The first component is mounted
  2. Mithril draws it immediately
  3. The second component is mounted, and queues a redraw
  4. The third component is mounted, and since a redraw is already queued, we stick with that one
  5. Mithril draws all three components

To avoid the immediate first draw, you can use m.startComputation before your initialisation code, and m.endComputation immediately afterwards. This will tell Mithril to pause and resume redraws. As you can see here, this will mean each component renders once during application initialisation.

may any other unexpected things happen too?

It depends upon expectation :) The Mithril philosophy is that rather than the traditional model of the programmer having to explicitly state when and how each individual part of the application redraws, you can just let Mithril compute all views automatically and update only the bits that have changed. So when component 1 draws a second time in your example, nothing is actually happening to its DOM. So there is actually nothing to worry about with this approach — if this were a real application, nothing bad would have happened. This becomes extremely useful when you have large models that can effect any number of views, because components are no longer responsible for explicitly stating when the model they represent may have changed — I can decide to add a new feature to my application like long-polling (making regular HTTP requests to get up to date data) and I wouldn't need to change any of my components: redraw is automatic and totally economic if no change to the view occurs.

@lhorie
Copy link
Member

lhorie commented Jun 21, 2015

@altunyurt as others mentioned, redraws are global because all code is considered part of a single tenant application. Consider this:

var things = m.request(...)

m.mount(foo, {
  view: function() {
    return m(".things", things().map(function(thing) {
      return m("div", thing.name)
    }))
  }
})


m.mount(bar, {
  view: function() {
    return m(".things", things().map(function(thing) {
      return m("div", thing.id)
    }))
  }
})

In this case, a single model entity is consumed by two different "islands" in the page, so when the data source updates, you logically would expect that both islands update simultaneously.

"Multi tenancy" is where you explicitly have two different, independent apps mounted to the same page, but where one does not affect the other in any way.

@altunyurt
Copy link
Author

Thank you all for the detailed explanations.

@dejayc
Copy link

dejayc commented Sep 28, 2017

@lhorie I'd like to know a little bit more about this philosophy. What do you propose as a solution for multi-tenant views where each view is complex and unrelated? For example, a source code repository browser might have a complex, computed dependency graph rendered on the top, and a user-input form at the bottom that supports real-time conversion of text to markdown. Independently, each part of the app might require considerable computation power to calculate the view. Forcing them to be single-tenant means updating the computed dependency graph each and every time a user enters a character into the text field, which seems like an expensive proposition.

@dead-claudia
Copy link
Member

@dejayc See #1907. Although it's for a different concern (subtree rendering), it does effectively this, and you're probably looking for subtree rendering, anyways.

You may also want to see this batch of utilities I made, which includes something meant for m.mount-independent rendering, and it was pretty much designed with that use case in mind (both libraries and complex views). It still redraws on global m.redraws, but you can avoid that by defining onbeforeupdate: function (vnode, old) { return vnode === old }.

@dejayc
Copy link

dejayc commented Sep 28, 2017

@isiahmeadows thanks for the feedback. I'm not looking to do plentiful customization of mithril at this point, so I'll probably return if performance actually becomes a problem. Is #1907 something that I could implement now without modifying mithril at all?

@dead-claudia
Copy link
Member

Not really without a lot of boilerplate (especially if you want proper batching), but my self-sufficient utility is probably what you're looking for. (That particular proposal only covers rendering individual roots, not full detachment.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants