Yet another micro package for reactive UI based on event-driven programming
Tags: javascript, frontend, showdev
Published: 2023-06-29 18:31:18

The ideas of this front-end micro package are:

- use **event-driven** programming: you write to the state and this triggers events with callbacks.

- write  HTML as strings: no templating, just normal Javascript interpolation,

- use datasets to references listeners on elements,

- declare state variables. They have properties such as:
  - `.val` which is a setter and a getter,
  - `.target` which sets the DOM element which will receive the event triggered by a state change,
  - `.resp` which sets desired rendering.

- diff on the data, not the DOM.

It gives a simple reactive pattern based on emitting events and limits the rendering of arrays with a diffing function _on the data_.

Take the example of a button to increment a counter. You have declared a state object "counter". The state is managed by the package.

```html
<button data-click="incr" type="button">Incr counter</button>
```

You have set up an "onclick" listener on this element via the dataset `data-click`. When clicked, it will run the function you declared:

```js
incr: ()=> counter += 1
```

This mutates the state. Another DOM element, say a `<p id="count">` should display the updated value. You target this element for this state variable, and set `counter.target= #count`. The state is managed by the package and will emit a change Event that targets this "#count" element.

If you set on this element an "onchange" listener - with a dataset `data-change="displayCounter"` - like:

```html
<p id="count" data-change="displayCounter"></p>
```

then the "onchange" listener of this element will react to the event and run the "displayCounter" function, which should generally return an HTML string element.

To finally render, we assign the output to the key `counter.resp` of the state. This will mutate again the "counter" state:


```js
displayCounter: ()=> counter.resp = `<span>${counter}</span>`
```

The package will take this state mutation into account and continue to a rendering function that eventually does something like:

```js
count.innerHTML = counter.resp
```

- when the state object is an array, calculate the diff _on the data_, between the new data and the old, and render only the differences (limited to the whole row): a "delete", a "new", an "append", an "update".


The micro package named "biny" implements these ideas. It is very close to Vanilla JS but modest in the sense that it is short (1.4kB, 250LOC) and cannot deal with computed or derived state. This can be easily managed by another state variable. It performs almost like vanilla JS when you run the JS-framework bench test. The code to write the famous todoMVC is also quite compact.


Source: <https://github.com/ndrean/binyJS>

A simple counter looks like:

```js
import B from "binyjs";

const counter = B.state({ val: 0 }),
  actions = B.Actions({
    inc: () => (counter.val += 1),
    display: () => {
      counter.target = count;
      counter.resp = `<span>binyJS state: ${counter.val}</span>`;
    },
  });

window.onload = () => actions.display();

app.innerHTML = `
  <div>
    <h1>Hello biny</h1>
    <div>
      <button type="button" data-click="inc">
      <span data-change="display" id="count"></span>
      </button>
    </div> 
  </div>
`;
```