Closed jadjoubran closed 3 years ago
cc @arthurevans
maybe make it clearer that there's an overhead of using it, to make it clearer when it's suitable
Some content I posted on Slack earlier:
The core difference between map
and repeat
is that repeat
maintains the state of rendered nodes. If you are shuffling components around that have state, like input
s or other web components, and you are not setting that state explicitly, map
will cause problems, where repeat
will not.
A simple example of this is a list of items with checkboxes. Unless the checked state is explicitly set in the template, using map
will cause the checks not to re-order when the input array changes.
If your nodes do not have any kind of state, or your array is append-only, it is very likely that map
is better than repeat
performance-wise.
Maintaining the order association is an additional requirement, and maintaining it comes at a cost.
Thanks @ruphin ... I added some generalities about performance in the docs:
However, I missed that point about stateful components, which is an important one.
@arthurevans Nice! I think there may be a typo in the employee example though:
const employeeList = (employees) => html`
<ul>
${repeat(employees, (employee) => employee.id, (employee) =>
html`<li>employee.familyName, employee.givenName</li>`}
</ul>`
Missing closing )
before the closing }
@jorenbroekema, @FluorescentHallucinogen already put out a PR to address that typo. #698
I am observing something with the use of repeat
, and I am not sure if it is a bug:
From the above example, if I do a re-sort of the employeeList
, then modify the employees
array (say, by deleting an item), and passing it back to the repeat
directive, nothing happens, i.e. nothing gets deleted from the UI.
Does repeat
behave correctly when elements are subsequently removed/added from the array by changing the array contents?
@ernsheong changing the items of the array doesn't necessary trigger a render. You can call this.requestUpdate()
after you have changed the order/removed items
@ernsheong if you change the reference
but not the value
, a re-render is not triggered. This is a common issue with arrays and objects that people face.
You could do what pshihn suggested, or do the following, for example in a setter:
render() {
return html`
<h1>Hello world!</h1>
<ul>
${this.employeeList.map(employee => html`<li>${employee}</li>`)}
</ul>
`;
}
set employeeList (value) {
this._employeeList = value;
}
or to simply delete the first item without needing a setter like that
const [myOldItem, ...rest];
this.employeeList = [...rest];
Or adding
this.employeeList = [...this.employeeList, 'Joe'];
Thanks all, I am aware of values vs references. I was using https://github.com/SortableJS/Sortable, and I guess repeat
(and lit
) is just too fragile to have another library mess with the DOM at the same time.
For drag-and-drop my current conclusion is I have no choice but to handcraft down to the Drag-and-Drop API level following the example given here https://github.com/Polymer/lit-html/issues/460#issuecomment-420250615 in order to eliminate the weirdest of bugs.
I'm running into this problem: https://github.com/Polymer/lit-html/issues/877#issuecomment-511003245
I have tried using the repeat directive instead of map, but the values of my textareas are still messed up.
@justinfagnani
My own confusion with repeat and what brought me to this issue is that I think the word 'repeat' does not match its purpose. It's responsible for 1) reordering elements in a list and 2) mutating a list by adding/removing elements. I understand that both repeat and Array.map don't toss out the underlying DOM elements but repeat retains their state. Why don't we call it 'list' instead of repeat as it is really a stateful list structure.
I think that would clear a lot of confusion, at least for me.
Day 1 with lit-html.
Technically, Array.prototype.map
also retains the state of the DOM elements.
The difference between Array.prototype.map
and repeat
is that repeat
is keyed. In other words, in repeat
each DOM element is associated with a unique key in the source array item.
Practically this means repeat
has the following behaviours:
In these cases Array.prototype.map
would retain all the original DOM elements (and their state) in the original order. It will only re-assign the dynamic values if appropriate.
Note that by default the key in repeat
is the index in the array, which effectively makes repeat
behave exactly like a more computationally expensive Array.prototype.map
. The key function is an optional argument in the repeat
directive, but you should never use repeat
without a key function.
@ruphin
You stated:
In these cases
Array.prototype.map
would retain all the original DOM elements (and their state) in the original order. It will only re-assign the dynamic values if appropriate.
But the docs seem to say something else:
In most cases, using loops or Array.map is an efficient way to build repeating templates. However, if you want to reorder a large list, or mutate it by adding and removing individual entries, this approach can involve recreating a large number of DOM nodes.
Can you please clarify?
I would think that due to absence of keys in case of Array.map the original DOM elements will have their state (e.g. value of input element) reset and new values are assigned.
Regardless, why do the docs say that Array.map would recreate the DOM nodes? I thought lit-html maintains a finite pool of DOM nodes that it recycles (to avoid excessive GC and element creation overhead)
Hmm.
Note to self: when updating repeat docs should also add a note about this issue: https://github.com/Polymer/lit-html/issues/1007 (which is related to directives in general, not repeat, although there's also a repeat-specific workaround).
The 2.0 site does a good job with this.
I learned from @kenchris that the
repeat
directive should only be used if we intend to "re-order" the items and that in other cases we can just use.map
I'm opening this issue to make the documentation with that regards clearer: template-reference#repeat
I'd be happy to send a PR, but I'm not sure how to word it in a clearer way, so I'm opening an issue first to get feedback