0.4.7 • Published 10 months ago

binyjs v0.4.7

Weekly downloads
-
License
-
Repository
github
Last release
10 months ago

BinyJS

It is a micro vanilla Javascript project of 1.4kB to help to write reactive UI based on event-driven programming.

npm bundle size

This project was made to handle arrays and calculate the diff on the data, as demonstrated in the "JS-framework" bench test and the todoMVC. The performance is very close to the dedicated Javascript code written for this test. It uses event-driven programming: you write to the state and this triggers events with callbacks.

It uses an immutable state and computes the diff to render the desired DOM elements.

You write HTML as strings with normal interpolation.

It uses state variables. They have the following properties:

  • .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 the desired rendering.

Instead of writing "event" listeners in your HTML (where "event" can be "click" or "submit" or "change" or "input"), you write a dataset and reference a function. For example, data-change="compute".

It uses the key "stateVariable".resp to set the rendered DOM elements.

It relies on unique keys; you need to use the key attribute in the HTML to identify each element of the rendered DOM array. You need to pass to the state variable the unique identifier you use in your data (eg key: "id"). Both "key" are different.

Limitations: it is not fully reactive in the sense that you need to create a separate state variable for computed state. For example, you have a list of todos, completed or not. Suppose you have a counter on the total completed todos as a state variable. In the action where you change the completion of a todo, you need to modify of the counter state accordingly for the counter to be reactive. The "todoMVC" demonstrates this.

Usage

You get state and Actions from the package. You instantiate your state and action functions.

import B from "binyjs"

const todos = B.state({val: [], key: "id"})
const actions = B.Actions({remove: ()=> ...})

Example Counter

We want to render this HTML string:

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

We build the state variable "counter", pass the actions into the "Actions" function and set up the target for this state variable. We render an HTML string when we pass it to the .resp key of the state variable.

const counter = B.state({ val: 0 }),
  actions = B.Actions({
    inc: () => (counter.val += 1),
    display: () => {
      // this action targets the "#count" element
      counter.target = count;
              ^^^
      counter.resp = `<span>binyJS state: ${counter.val}</span>`;
              ^^^
    },
  });
// display the initial state on load.
window.onload = () => actions.display();

Ingredients

The state is required to be immutable. The main ingredients are:

  • state variables You need to instantiate them. If say "data" is a state variable in the form of a collection of objects [{id: 1, label: "..."},...], then data.val is a setter and getter. When your data is an array, set the unique identifier used by your data to the key "key".
const todoState = B.state({val: [], key: "id"})
data.val = ...
  • actions declared in the function B.Actions. You need to set the target to the state variable used in the action.
const actions = B.Actions({
  removeLi: ()=> {
    // the action targets the element "#ulis"
    todoState.target = ulis;
    ...
  },
  ...
})
  • key As a convention, Biny uses the attribute key in the HTML string when you render a collection: you need to declare key="${id}" if your data uses "id" as unique identifier. Note that the "is important for the querySelectors. Use it in your selectors.
const TodoItem = ({ id, label }) =>
  `<li key="${id}">
       ^^  ^     ^ 
  <span style="display:flex;">
  <label style="margin-right:10px;">${label}</label>
  <input type="checkbox"/>
  </span>
  </li>`;
  • data-event Biny uses the convention data-event for an "event" listener. This means you use data-click="addItem" when the element emits a click event that should run the "addITem" action declared in your "Actions".
  • You can use global listeners (element.addEventListener).
  • target Inside your listener, you must declare the target for each reactive state variable. It looks like data.target=tbody. You can also declare extra dependencies via a dataset if your component requires to read data hardcoded in the DOM and read them.

  • state.resp You need to return the data, normally HTML strings in the key ".resp".

  • data-change is the default callback. For example, you have a form with a data-submit:

<form id="fm" data-submit="addItem"></form>

This triggers the action below:

addItem: (e) => {
  e.preventDefault();
  todoState.target = ulis;
  todoState.val = [...todoState.val, { id: ++i, label: inputState.val }];
  fm.reset(), (inputState.val = "");
},

Since we send a new state, we want to render this new state. The target element "#ulis" contains a data-change="display":

<ul id="ulis" data-change="display"></ul>

Biny will run the callback "display":

display: () => {
  todoState.target = ulis;
  todoState.resp = todoState.val.map((todo) => TodoItem(todo));
},

Reactivity pattern

We use the simple event loop and a "diffing function" to detect the 6 following changes made to the state: "assign", "append", "clear", "remove", "update" and "swap" (rows).

Test

The performance is close to the Vanilla JS code specific for this test to which we compare the Biny package.

Examples

The code for the examples:

0.4.7

10 months ago

0.4.5

11 months ago

0.4.4

11 months ago

0.4.6

11 months ago

0.4.3

11 months ago

0.3.0

11 months ago

0.2.0

11 months ago

0.3.5

11 months ago

0.4.1

11 months ago

0.3.2

11 months ago

0.4.0

11 months ago

0.3.1

11 months ago

0.3.4

11 months ago

0.4.2

11 months ago

0.3.3

11 months ago

0.1.2

11 months ago

0.1.1

11 months ago

0.1.0

11 months ago