Yet another micro package for reactive UI based on event-driven programming
NDREAN
Posted on June 29, 2023
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.
<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:
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:
<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:
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:
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:
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>
`;
Posted on June 29, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
June 29, 2023