007. Librería de Componentes

Esta sección está siendo el preámbulo para ver como funcionan librerías y frameworks como Angular, React, Vue. En el capítulo anterior vimos como trabajar un estado local, donde el state global tendrá todas las variables que se necesiten para el funcionamiento de la aplicación, pero que cada componente de UI puede tener sus propios estados locales. En el capítulo anterior creamos un ejercicio al vuelo, pero lo interesante sería crear una librería externa y generar tantas instancias como necesitemos, con lo que también trabajaríamos con el principio DRY (don´t repeat yourself).

Vamos a crear un archivo denominado 05_libreria-componentes-con-estado.html y un archivo denominado Component.js, que tendrán la siguiente sintaxis.

Sintaxis de 05_libreria-componentes-con-estado.html.

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Librería de componentes con estado</title>
  </head>

  <body>
    <h1>Librería de componentes con estado</h1>

    <form id="todo-form">
      <input type="text" id="todo-item" placeholder="Tarea por hacer" />
      <input type="submit" value="Agregar" />
    </form>

    <h2>Lista de tareas</h2>

    <ul id="todo-list"></ul>

    <script src="./Component.js"></script>
    <script>
      const d = document;

      const app = new Component({
        el: "#todo-list",
        data: {
          todoList: [],
        },
        template: function (props) {
          if (props.todoList.length < 1) {
            return `<p><em>Lista sin tareas por hacer</em></p>`;
          }
          let todos = props.todoList.map((item) => `<li>${item}</li>`).join("");
          return todos;
        },
      });

      d.addEventListener("DOMContentLoaded", app.render);

      // Estableciendo valores por defecto al state
      app.setState({
        todoList: ["Tarea 1", "Tarea 2", "Tarea 3"],
      });

      d.addEventListener("submit", (e) => {
        if (!e.target.matches("#todo-form")) return false;
        e.preventDefault();
        const $item = d.getElementById("todo-item");
        if (!$item) return;

        // Actualizar el State de forma reactiva
        const lastState = app.getState();
        lastState.todoList.push($item.value);
        app.setState({
          todoList: lastState.todoList,
        });

        // Limpiar el input
        $item.value = "";
        $item.focus();
      });
    </script>
  </body>
</html>

Sintaxis de Component.js.

const Component = (function () {
  // Creamos el constructor de este Componente
  const Constructor = function (options) {
    this.el = options.el;
    this.data = options.data;
    this.template = options.template;
  };

  // Agregar los métodos al prototipo del constructor del componente

  // Render UI
  Constructor.prototype.render = function () {
    const $el = d.querySelector(this.el);
    if (!$el) return;
    $el.innerHTML = this.template(this.data);
    console.log(this.data);
  };

  // Actualizar el State de forma reactiva
  Constructor.prototype.setState = function (obj) {
    for (let key in obj) {
      if (this.data.hasOwnProperty(key)) {
        this.data[key] = obj[key];
      }
    }
    this.render();
  };

  // Obtenemos una copia inmutable del State
  Constructor.prototype.getState = function () {
    return JSON.parse(JSON.stringify(this.data));
  };
  return Constructor;
})();
Scroll al inicio