I’m having trouble with templates rerendering (I believe) too often, which is causing slowdown on my page.
Here’s the setting: I have a recursive template for rendering a message, which recursively renders children of the message. Messages are documents in a MongoDB. Each message document has a children
field that lists the _id
fields for all children messages. Naturally, I use a helper children
that looks up the children in the Messages database:
children() { return Messages.find({_id: {$in: this.children}}) }
This seems to cause excess rendering trouble, though, which looks fine but can cause severe slowdowns for large message trees. Whenever a message document changes (not just the children
field, but any field), all the children templates get rerendered (or, at least, all the helpers get called again). I assume this is because the find
is executing a fresh search each time. Even adding a .fetch()
at the end of the children
helper doesn’t help. This surprises me, as the Reactivity Model for Each suggests that it should realize that the data is the same so not rerender… Is this intended behavior?
Here is a more complete example, where an “edit” button enables artificial edits, and a template variable renders
keeps track of how many times the template gets rendered.
<h1>Messages</h1>
{{#with root}}
{{> message}}
{{/with}}
<template name="message">
<p>Message {{_id}} with <b>{{edits}} edits</b> and <b>{{renders}} renders</b> <button class="edit">Edit</button></p>
<ul>
{{#each children}}
<li> {{> message}} </li>
{{/each}}
</ul>
</template>
Template.message.onCreated ->
@renders = 0
Template.registerHelper 'root', ->
Messages.findOne
root: true
Template.message.helpers
renders: ->
Template.instance().renders += 1
children: ->
Messages.find _id: $in: @children ? []
Template.message.events
'click button.edit': (event, instance) ->
Messages.update instance.data._id,
$inc: edits: 1
event.preventDefault()
event.stopPropagation()
In writing this post, I did discover one way to avoid the rerendering, which is to lookup inside the each
instead of during the each
helper:
<template name="message">
<p>Message {{_id}} with <b>{{edits}} edits</b> and <b>{{renders}} renders</b> <button class="edit">Edit</button></p>
<ul>
{{#each children}}
{{#with lookup}}
<li> {{> message}} </li>
{{/with}}
{{/each}}
</ul>
</template>
Template.message.helpers
renders: ->
Template.instance().renders += 1
lookup: ->
Messages.findOne @valueOf()