Skip to content
Advertisement

How to achieve a maintainable, reactive UI with vanilla JS

today I’ve got a problem which would be easily solved by using a reactive and state managed framework like Vue. Sadly, its not posssible to use it.

Following (simplified) situation (link to codepen): We’ve got a server rendered page which has a price field. It has the opportunity to add or remove a note. If we add a note, its posted to the server and the UI should update itself. Same for removing a note.

const priceField = document.getElementById("priceField");

priceField.querySelector("#create-note-btn").addEventListener("click", () => {
  priceField.querySelector("#note-input-row").classList.toggle("hidden");

  // depending on state #create-note-btn can hide/show #note-row or #node-input-row
});

priceField.querySelector("#submit-note-btn").addEventListener("click", () => {
  priceField.querySelector("#note-row").classList.toggle("hidden");
  priceField.querySelector("#note-input-row").classList.toggle("hidden");

  const input = priceField.querySelector("input").value;
  priceField.querySelector("#note").innerHTML = input;
  // api call
  // further ui updates, like changing icon of #create-note-btn
});

priceField.querySelector("#delete-note-btn").addEventListener("click", () => {
  priceField.querySelector("#note-row").classList.toggle("hidden");
  // api call
  // resetting icon of #create-note-btn
});

// much more logic with additional UI update operations, like recalculation of price etc.
.hidden {
  display: none;
}
<div id="priceField">
  <div>
    <span>100 €</span>
    <button id="create-note-btn">Create Note</button>
  </div>
  <div id="note-input-row" class="hidden">
    <input></input>
    <button id="submit-note-btn">Submit</button>
  </div>
  <div id="note-row" class="hidden">
    <span id="note">Placeholder note</span>
    <button id="delete-note-btn">Delete Note</button>
  </div>
</div>

To achieve this (only!) javascript is used to update the view. Therefore many classlist.toggle("..") calls or other things are done to display / hide elements. Additionally,there is a bunch of different operations which does also update the view on different places.

To keep the code maintainable I want to achieve, that the UI update is done at one central place and not split across different calls. Also state should be kept for page reload.

Whats an easy and maintable way to do so?

My thoughts: Implement a little state machine (INITIAL, OPEN_NOTE, CLOSED_NOTE, …) and a render()-function which depends on actual state. To keep changes for page reload, localstorage has to be used or server side rendered html needs to stateful aswell.

Advertisement

Answer

I followed my thoughts by implementing internal state with a render-function which holds all UI related changes.

const RenderMode = {
  INITIAL: "Initial",
  CREATE: "Create",
  OPEN: "Open",
  SHOW_NOTE: "Show note input",
  TOGGLE_PRICE: "Toggle price input",
};

render() {
    switch (this.renderMode) {
      case RenderMode.INITIAL:
        this._hideIndicatorIcon();
        this._hideIndicatorRow();
        this._hideInputRow();
        this._hidePriceInput();

        break;
      case RenderMode.CREATE:
        this._showInputRow();
        break;
      case RenderMode.OPEN:
        this._showIndicatorIcon();
        this._hideInputRow();
        this._hideIndicatorRow();
        break;
      case RenderMode.SHOW_NOTE:
        this._showIndicatorRow();
        break;
      case RenderMode.TOGGLE_PRICE:
        this._togglePriceInputs();
        break;
      default:
        console.error("No render mode defined!");
    }

State after page reload is determined from custom attributes of server side rendered html:

  initializeRenderMode() {
    ...
    // if note already exists on page load switch render mode to open
    this.renderMode = existingNote ? RenderMode.OPEN : RenderMode.INITIAL;
    this._render();
  }

Its by far not the best solution, but it helps me to keep things simple.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement