Frame

The State defines a list of user-defined components, but we need a way to render those components. But before we can do that, we need to know what to render.

Frame is essentially an instance of a component defined in the State and it outputs a View

For example, let's say we created a Frame for the Button component above with its enabled prop set to true. Then, its output View would be the following:

tsx
{
type: "TagView",
tag: "button",
props: {
className: "bg-blue-900",
},
children: [
{
type: "TagView",
tag: "text",
props: {
value: 0
},
},
{
type: "TagView",
tag: "text",
props: {
value: "I'm enabled!"
}
}
]
}

The View is essentially the resulting static output of a component. The View of a Frame is recomputed when the component’s definition in the state is updated or when a side effect (ie: mutating a global variable) is performed.

Keeping the View updated

Apart from generating the View of a component from the state, a Frame is also responsible for keeping the View updated on any changes made to the state.

For example, let's say we were to update a prop to the above Button component's root <button> element:

tsx
{
type: "RekaComponent",
props: [...],
state: [...],
template: {
type: "TagTemplate",
tag: "button",
props: {
"className": {
type: "Literal",
// value: "bg-blue-900",
value: "bg-green-900" // <-- updated value
}
},
children: [...]
}
}

Then, the resulting output will be updated accordingly:

tsx
{
type: "TagView",
tag: "button",
props: {
className: "bg-green-900", // <-- updated
},
children: [...]
}

Rendering

When we create a Frame for an instance of a component defined in the state, we now know what to render - the View. Now all that's left is - how to render the given View.

Given that the View is essentially a serializable JSON structure, we can easily build a renderer that takes the View and renders it in whatever framework we want. For example, we can easily create a React renderer like so:

tsx
const Renderer = ({ view }) => {
if (props.view instanceof t.TagView) {
if (props.view.tag === 'text') {
return <span>{props.view.props.value}</span>;
}
return React.createElement(
props.view.tag,
props.view.props,
props.view.children.map((child) => (
<Renderer key={child.id} view={child} />
))
);
}
if (props.view instanceof t.RekaComponentView) {
return props.view.render.map((r) => <Renderer key={r.id} view={r} />);
}
if (props.view instanceof t.ExternalComponentView) {
return props.view.component.render(props.view.props);
}
if (props.view instanceof t.SlotView || props.view instanceof t.FragmentView) {
return props.view.children.map((r) => <Renderer key={r.id} view={r} />);
}
if (props.view instanceof t.ErrorSystemView) {
return (
<div className="">
Something went wrong. <br />
{props.view.error}
</div>
);
}
return null;
};
Made with by @prevwong
Reka is released under the MIT license