Skip to content

Passing props

Components take props the way elements do. A parent passes values on the tag, and the child declares them as a typed parameter:

src/quest-log.ts2
{QUESTS.map((quest, i) => (
<QuestEntry name={quest.name} state={quest.state} y={i * ROW_H} />
))}
src/quest-log.ts2
const QuestEntry = ({ name, state }: { name: String; state: QuestState }): Component => (
<text x={4} w={{ fill: 8 }} h={ROW_H} text={name} font={Fonts.PLAIN}
valign="middle" shadow
color={state === QuestState.COMPLETE ? Colors.GREEN
: state === QuestState.IN_PROGRESS ? Colors.YELLOW : Colors.RED}
options={['Read journal']} optionSubject={name} />
);

QuestEntry declares no y, yet the call site passes one.

A prop the component does not declare is applied to its root element. Here y={i * ROW_H} lands on the <text>. This works for anything the root takes: geometry, hide, visual props, options, hover, a ref.

src/quest-log.ts2
<Stat label="Quest points" value={27} y={0} />
<Stat label="Quests" value={QUESTS.length} color={Colors.DIM} y={18} />
  • The caller wins. A caller’s w={100} overrides the root’s own w="fill".
  • A root that is another component passes the props on to its own root.
  • A position is a pair. A caller that gives y alone keeps the root’s own x, so the root must write one, as QuestEntry’s x={4} does.

A longer props type reads better as an interface. Doc comments show when the prop is hovered in an editor:

interface QuestEntryProps {
/** The quest's name, as the journal spells it. */
name: String;
state: QuestState;
}
const QuestEntry = ({ name, state }: QuestEntryProps): Component => { … };

A default makes the prop optional:

src/quest-log.ts2
const Stat = ({ label, value, color = 0xffffff }: {
label: String; value: Int; color?: Int;
}): Component => (

A default is a literal: 0, -1, '', 0xffffff.

Give a prop the game type it holds, not Int:

interface IconProps {
icon: Sprite;
}

Types lists them all. A record you declared is a prop type too; see Data.

Markup between the tags arrives as the children prop:

src/quest-log.ts2
<SunkenBox>
<layer ref={list} x={2} y={2} w={{ fill: 22 }} h={{ fill: 4 }}
scrollHeight={QUESTS.length * ROW_H}>
{/* … the rows … */}
</layer>
</SunkenBox>

Composing components covers it.

Compute from props rather than assigning to them. A value that changes over time is state.

const QuestEntry = ({ name, state }: QuestEntryProps): Component => {
state = QuestState.COMPLETE; // no
const label = state === QuestState.COMPLETE ? name : `${name} …`; // yes
…
};

A component does not take a handler as a prop. Put the handler on a <layer> around the call:

src/shop.ts2
<layer x={i * 79} y={0} w={74} h={22} onClick={() => { setTab(i); }}>