Rendering lists
.map() turns each entry of a list into a component. There are three kinds of
list, told apart by their type: a constant array, a collection, and a container
(why three).
A constant array
Section titled “A constant array”A plain array literal is a constant array:
const TABS = ['Buy', 'Sell', 'Info'];{TABS.map((label, i) => ( <layer x={i * 79} y={0} w={74} h={22} onClick={() => { setTab(i); }}> {/* … the button … */} </layer>))}The callback gets the entry and its index. Entries may be records, and each
field keeps its type. An enum is a constant too.
A collection
Section titled “A collection”A list or map you declared, or one of the game’s tables, is a collection:
{PRAYERS.map((prayer, i) => ( <PrayerCell prayer={prayer} unlocked={prayer.level <= level} x={(i % COLUMNS) * CELL} y={(i / COLUMNS) * CELL} />))}Arithmetic is whole-number, so i / COLUMNS is the row. A map’s callback takes
the value, its key and its visible position:
{CategoryNames.map((label, category, i) => ( <text x={i * 76} y={0} w={72} h={14} text={label} font={Fonts.SMALL} valign="middle" shadow color={category === Category.TOOLS ? Colors.WHITE : Colors.DIM} />))}PRAYERS.length is the number of entries.
The test goes inside the row
Section titled “The test goes inside the row”PrayerBook reads the player’s level, and each cell chooses between a lit
sprite and a faded one:
<layer x={x} y={y} w={CELL} h={CELL}> {unlocked ? <sprite x="center" y="center" w={34} h={34} sprite={prayer.icon} options={['Activate']} optionSubject={prayer.name} /> : <sprite x="center" y="center" w={34} h={34} sprite={prayer.icon} transparency={190} />}</layer>Skipping entries
Section titled “Skipping entries”.filter() narrows a collection or a container before you map it:
{PRAYERS .filter((prayer) => prayer.level <= STARTING_LEVEL) .map((prayer, i) => ( <sprite x={92 + i * 20} y="center" w={18} h={18} sprite={prayer.icon} /> ))}The .map() index counts only what survived, so the strip closes up. The
filter’s own second parameter is the position before anything was dropped:
(prayer, i) => i < 4 keeps the first four.
Use a conditional inside the row instead when the entry must keep its place, as an empty bank slot does.
Carrying a running total
Section titled “Carrying a running total”.scan() is .map() carrying a sum, for rows of uneven height:
{WARES.scan( (ware, i, y) => <WareRow ware={ware} coins={coins} y={y} />, (ware) => rowHeight(ware), 4,)}The second argument is what this row adds; the third is the start, default 0.
The callback’s last parameter is the total as the row began, its top edge.
Inventories
Section titled “Inventories”A container the server owns maps by slot number, empty slots included:
{inventory.map((item, slot) => ( <Slot item={item} count={inventory.count(slot)} slot={slot} />))}An empty slot holds -1. A container filters like a collection.
Positioning rows
Section titled “Positioning rows”Position the rows of a collection or a container from the index or a running total:
.map((row, i) => <Row y={i * 24} … />)Flow layout works for a constant array.