Components are functions for rendering parts of the UI:
1linkexport function Greetings({to}, renderer) {
2link return <h1>Hellow {to}!</h1>;
3link}
1linkimport { Greetings } from './greetings';
2link
3linkrenderer.render(
4link <Greetings to='World'/>
5link).on(document.body);
1linkconst records = state([]);
2linkconst add = () => records.set(records.get().concat(new Date()));
3linkconst clear = () => records.set([]);
4link
5linkfunction Record({ record }, renderer) {
6link const remove = () => records.set(records.get().filter(r => r !== record.get()));
7link
8link return <div>{ record } <button onclick={remove}>X</button></div>
9link}
10link
11linkrenderer.render(
12link <>
13link <button onclick={add}>Add</button>
14link <button onclick={clear}>Clear</button>
15link <List of={records} each={record => <Record record={record}/>}/>
16link </>
17link).on(document.body);
verified_user TYPE SAFETY
If you are using TypeScript, it is recommended to properly type-annotate your components for easier and safer re-use:
1linkimport { Source } from 'callbag-common';2linkimport { RendererLike } from 'render-jsx';3link4linkexport interface GreetingsProps {5link to: string | Source<string>6link}7link8linkexport function Greetings(props: GreetingsProps, renderer: RendererLike<Node>) {9link return <h1>Hello {props.to}!</h1>;10link}
Component function names MUST start with uppercase letters:
1link// 🚫 WRONG:
2linkfunction myComponent(...) { ... }
3link
4link// ✅ CORRECT:
5linkfunction MyComponent(...) { ... }
The second argument of a component function MUST be named renderer
:
1link// 🚫 WRONG:
2linkfunction MyComp() { return /*~*/<h1>~~~~Hellow!~~~~~~~</h1>~~~~~/*~*/ } // --> renderer is not defined
3link
4link// ✅ CORRECT:
5linkfunction MyComp(_, renderer) { return <h1>Hellow!</h1> }
Child elements are passed to component functions as the third argument:
1linkexport function Card(_, renderer, children) {
2link return <div style={{
3link display: 'inline-block',
4link 'border-radius.px': 7,
5link 'padding.px': 16,
6link 'box-shadow': '0 3px 6px rgba(0, 0, 0, .25)',
7link }}>
8link {children}
9link </div>;
10link}
1linkimport { Card } from './card';
2link
3linkrenderer.render(
4link <Card>
5link Hellow There!
6link </Card>
7link).on(document.body);
👉 children
is an array of child elements passed to the component:
1linkexport function UnorderedList(_, renderer, children) {
2link return <ul>
3link {children.map(child => <li>{child}</li>)}
4link </ul>;
5link}
1linkimport { UnorderedList } from './unordered-list';
2link
3linkrenderer.render(
4link <UnorderedList>
5link Hellow There!
6link <span>How are you doing?</span>
7link <div>Huh this is relatively neat</div>
8link </UnorderedList>
9link).on(document.body);
👉 For named child slots, it is recommended to use properties:
1linkexport function UnorderedList({footer}, renderer, children) {
2link return <>
3link <ul>
4link {children.map(child => <li>{child}</li>)}
5link </ul>
6link { footer || '' }
7link </>;
8link}
1linkimport { UnorderedList } from './unordered-list';
2link
3linkrenderer.render(
4link <UnorderedList footer={<>This is the end</>}>
5link Hellow There!
6link <span>How are you doing?</span>
7link <div>Huh this is relatively neat</div>
8link </UnorderedList>
9link).on(document.body);
Component functions are called only once, when they are used within JSX:
1linkconst Z = <MyComp x={42}/>
☝️ This is roughly equivalent to the following:
1linkconst Z = MyComp({ x: 42 }, renderer)
A component has two other important life-cycle events:
You can hook into these events using hooks:
1linkfunction MyComponent(...) {
2link this.onBind(() => ...)
3link this.onClear(() => ...)
4link}
👉 More conveniently, you can track callbags (or subscriptions) between these two events (i.e. while created elements are on screen):
1linkexport function JinxCheck({ source }, renderer) {
2link this.track(pipe(
3link source,
4link subscribe(v => {
5link if (v === 13) {
6link alert('JINX!');
7link }
8link }),
9link ));
10link
11link return <small>Jinx-Check is Active!</small>
12link}