The concept of temporary View state in JavaScript

Hello everyone! In this article, we will talk about a rather unusual topic, information about which for some reason I did not find, although it is quite useful in modern JavaScript frameworks and libraries for creating user interfaces, because, in some cases, the use of the concept can help speed up work with DOM several times.

The name is arbitrary, but the essence is important.

The problem of the ordinary state

The term “regular state” refers to data that is stored directly by state managers or by the internal functionality of a framework or library. An example of state in Vue.js:

createApp({
  setup() {
    return {
      count:ref(0);
    };
  },
  template: `<div>
        <button @click="count++">Click!</button>
        <div>Clicks: {{ count }}</div>
    </div>`,
}).mount("#app");

In this case, the state is stored directly in the object that is returned in the framework's predefined method.

Now, DOM nodes can depend on this state through different syntactic constructions. In the example, such a construction is the line {{ clicks }} which is changed to the current data thanks to row interpolation.

Also, a frequently used syntactic construct is “loop”. A loop is a keyword, or attribute, or method that explicitly specifies that DOM nodes will be created, depending on the number of elements and the values ​​themselves, coming from the state. In this article, I consider this topic more specifically. An example of a loop:

<template>
  <tr
    v-for="{ id, label } of rows"
    :key="id"
    :class="{ danger: id === selected }"
    :data-label="label"
    v-memo="[label, id === selected]"
  >
    <td class="col-md-1">{{ id }}</td>
    <td class="col-md-4">
      <a @click="select(id)">{{ label }}</a>
    </td>
    <td class="col-md-1">
      <a @click="remove(id)">
        <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
      </a>
    </td>
    <td class="col-md-6"></td>
  </tr>
</template

Let's say we want to update class element. Our data comes in the form of an array of objects. It is clear that in the object you need to explicitly specify the key:class value and in double curly brackets get everything by key, but this is the main problem, because it is slow.

To confirm my words, I will take benchmarks of the cample.js framework (in the process of developing which I noticed a similar problem). It is clearly seen there that a class that depends directly on the data of the normal state is installed slower than a class that uses a temporary View state.

Let's take two versions of cample.js: 3.2.0-alpha.45 and 3.2.1-beta.0. There is a line called “select row” (line 4), and that is where the main difference lies:

1. Comparison of 3.2.0-alpha.45 and 3.2.1-beta.0 in terms of speed.

1. Comparison of 3.2.0-alpha.45 and 3.2.1-beta.0 in terms of speed.

Data taken from 126 And 128 benchmark releases.

As you can see from the image, the difference between one result and the other is almost one and a half times. I thought for a long time about why this is so? I used to assume that the code was simply slow, but the fact is different. If the data goes through a regular state, then it becomes necessary to go through all the data, even if we only change one letter in the value of a property in an n-order object.

const oldData = [
  {
    id: 1,
    label: "Текст 1",
  },
  { id: 2, label: "Текст 2" },
  {
    id: 3,
    label: "Текст 3",
  }
];

const newData = [
  {
    id: 1, 
    label: "Текст 11", // 1 итерация поменялась одна буква, но всё равно смотрим дальше
  },
  { id: 2, label: "Текст 2" }, // 2 итерация
  {
    id: 3,
    label: "Текст 3", // 3 итерация
  }
];

So, it will always be slow, but it is a logically correct approach and this is the main joke of all modern frameworks and libraries for creating user interfaces. But, what could be the alternative to this approach?

Temporary View state

Especially for such a problem, when it is necessary to introduce a separate state from the main one, so as not to go through the elements several times, you can use a certain concept in the code that will allow you to bind not to an object, but to an element. This concept is a temporary View state.

The gist of it is this: We create a separate array for each element. It will be in the code of the module itself, and we give the user methods that interact with this array in the callback function. Thus, the module will store approximately the following code:

{
  el:li,
  temporaryViewState:[{class:"value"}]
}

And on the client it looks something like this:

setClass: [
  (setData, event, eachTemporaryViewState) => () => {
    const { setTemporaryViewState, clearTemporaryViewState } = eachTemporaryViewState;
    clearTemporaryViewState();
    setTemporaryViewState(() => {
      return { class: "value" };
    });
  },
  "updateClass",
],

Also, this array can be created only when the callback function is called, or simply create one array for all elements. This will allow us not to bind to the data that comes from the normal state, but to bind to a specific element that we want to update. That is, we create a temporary state that can be cleared and rewritten. This is well suited for those cases when we want to work with uncontrolled elements:

<!-- Контролируемый -->
<input type="text" value="{{ value }}" ::change="setValue()" />

<!-- Неконтролируемый -->
<input type="text" class="{{ temporaryViewState.class }}" />

That is, it simply does not depend on the normal state directly, therefore, in the DOM, this node, one might say, will be static (if we make a node template, then this element will skip).

Thus, we have a state that depends only on a specific element and a callback function. When working with a “loop”, you don't have to go through the entire data array to update one letter in one element. It will be enough to simply call a specific function on a specific element and update a specific class.

This will allow you to achieve quick results in working with data and DOM. This concept can be easily applied in modern frameworks and libraries and worked with.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *