Repeat Utility
The repeat
utility is Markup recommended way to render iterable data or repeating content. It handles things like caching and tracking for the template ensuring list changes only happen to the items that need them.
html`${repeat(todos, renderTodo)}`
Why use repeat?
Markup templates already handle arrays but like any other injected value, Markup track the whole thing and not its individual items.
const [todos, updateTodos] = state([])
html`${() => todos().map(renderTodo)}`.render(document.body)
updateTodos((prev) => [
...prev,
{
name: 'Go to gym',
status: 'pending',
},
])
The above code will work just fine as far as rendering a list. The issue becomes evident when a single item changes, is added or removed. This is because the function will get called again generating a brand new list of content to render. If Markup sees a new list, it will re-render everything.
The better way is to not dynamically map the list on render but on creation so Markup always have the things you want to render so it can check if its different from before or not.
const [todos, updateTodos] = state([])
html`${todos}`.render(document.body)
updateTodos((prev) => [
...prev,
renderTodo({
name: 'Go to gym',
status: 'pending',
}),
])
This is exactly what repeat
utility does for you by just taking the data and the render function.
Numbers
The repeat
helper accepts a number among many things. This number represents the number of times you want the callback render function needs to be called.
html`${repeat(3, html`<spa></span>`)}`
The callback function will be called with numbers from 1 to the number you provided along with the indexes.
html`${repeat(3, (n, index) => html`<spa>${n} - ${index}</span>`)}`.render(
document.body
)
// <span>1 - 0</span><span>2 - 1</span><span>3 - 2</span>
Iterables and Object literals
Additionally, repeat
can consume any Object literal or iterable object and this includes:
- Array
- Set
- Map
- String
- any object with Symbol.iterator
const iterable = {}
iterable[Symbol.iterator] = function* () {
yield 1
yield 2
yield 3
}
html`${repeat(iterable, renderItem)}`
Since Markup template do not handle rendering such objects (except Array), this is an additional advantage in using repeat
. You dont have to worry about converting any data into Array just so you can iterate and render.
The callback function will always get called with the entries and the index.
const employeesSalary = {
'john doe': 30000,
'jane doe': 54000,
}
html`${repeat(employeesSalary, ([name, salary], index) => html`...`)}`
Empty state
The repeat
also consumes an optional third argument which is a function that will get called to return what to render when the data is empty.
html`${repeat(todos, renderTodo, () => html`<p>No todos yet!</p>`)}`