Getting started

Open in StackBlitz

Installation

Before we start, we need to install 2 packages:

  • @rekajs/types which provides APIs to create Reka data types (ie: the State AST nodes)
  • @rekajs/core which allows us to create a new Reka instance
npm install @rekajs/types @rekajs/core

Define a new State

First, we need a new Reka instance which requires a State data type that will be used to store the components and global variables created by the end user.

For now, we will create an initial State type with a simple App component:

tsx
import { Reka } from '@rekajs/core';
import * as t from '@rekajs/types';
const reka = Reka.create();
reka.load(
t.state({
program: t.program({
globals: [],
components: [
t.rekaComponent({
name: 'App',
props: [],
state: [],
template: t.tagTemplate({
tag: 'div',
props: {},
children: [
t.tagTemplate({
tag: 'text',
props: {
value: t.literal({
value: 'Hello World!',
}),
},
children: [],
}),
],
}),
}),
],
}),
})
);

The above component in the State is equivalent to the following React component:

tsx
const App = () => {
return <div>Hello World</div>;
};

Creating a Frame

Next, let's create a new Frame to evaluate an instance of our newly created App component from above:

tsx
const reka = Reka.create(...);
const frame = await reka.createFrame({
id: 'my-app-component',
component: {
name: 'App',
props: {}
}
});

The Frame instance computes a View which is the resulting render output of a component's instance:

tsx
const view = frame.view;
// view =
{
type: "RekaComponentView",
render: [{
type: 'TagView',
tag: 'div',
props: {},
children: [
{
type: 'TagView',
tag: 'text',
props: {
value: 'Hello World!',
},
children: []
}
]
}]
}

Mutating the State

Now that we have a working Reka instance with a valid State, let's try to make some changes to it.

Changes made to the State must be wrapped with the .change() method.

For example, let's add a new <button> element to our App component:

tsx
const appComponent = reka.program.components[0];
reka.change(() => {
appComponent.template.children.push(
t.tagTemplate({
tag: 'button',
props: {},
children: [
t.tagTemplate({
tag: 'text',
props: {
value: t.literal({ value: 'Click me!' }),
},
children: [],
}),
],
})
);
});
console.log(appComponent.template.children[1]);
// console:
{ type: "TagTemplate", tag: "button", props: {}, children: [...] }

Views are automatically updated

Earlier, we created a Frame instance, but what happens to its View when we performed the above mutation?

Well, its View is automatically updated to reflect the changes made in State:

tsx
// from previous example
const frame = await reka.createFrame(...)
reka.change(() => {...});
console.log(appComponent.template.children[1]);
console.log(frame.view);
// console:
{
type: "RekaComponentView",
render: [{
type: 'TagView',
tag: 'div',
props: {},
children: [
{
type: 'TagView',
tag: 'text',
props: {
value: 'Hello World!',
},
children: []
},
// View has been updated to contain the following child View
// as a result of the mutation to add a new <button> TagTemplate in the State
{
type: 'TagView',
tag: 'button',
props: {},
children: [
{
type: 'TagView',
tag: 'text',
props: { value: 'Click me!' },
children: []
}
]
}
]
}]
}

Subscribing to changes

Oftentimes, it would be pretty useful to know when there's a change to a Reka data structure (ie: the State or View):

tsx
reka.watch(() => {
if (appComponent.template instanceof t.TagTemplate) {
console.log('appComponent =>', appComponent.template.tag);
}
});
reka.change(() => {
// Since we know the type of appComponent.template, we can use t.assert to assert the type
t.assert(appComponent.template, t.TagTemplate).tag = 'section';
});
// 1)
// console:
// appComponent => section
reka.change(() => {
t.assert(appComponent.template, t.TagTemplate).tag = 'div';
});
// 2)
// console:
// appComponent => div

The same can be done in order to watch for changes made to a resulting View:

tsx
reka.watch(() => {
if (frame.view) {
console.log(
'frame root tag =>',
t.assert(frame.view.render[0], t.TagView).tag
);
}
});
reka.change(() => {
t.assert(appComponent.template, t.TagTemplate).tag = 'section';
});
// console:
// frame root tag => section
Made with by @prevwong
Reka is released under the MIT license