Integrasi Reactive System dengan DOM Renderer (Auto Update UI)

By | 19 October 2025

Dalam pengembangan aplikasi modern, sistem reaktif menjadi inti dari banyak framework populer seperti Vue.js, React, dan Svelte. Konsep dasarnya adalah membuat data dan tampilan (UI) selalu sinkron tanpa perlu menulis ulang logika render secara manual. Ketika data berubah, tampilan akan otomatis menyesuaikan, inilah esensi dari reactivity-driven UI update.

Setelah sebelumnya kita membahas Reactive System dengan Proxy, Reflect, dan Dependency Tracking, langkah selanjutnya adalah melakukan integrasi reactive system dengan DOM renderer. Dengan pendekatan ini, JavaScript murni dapat menghasilkan antarmuka dinamis layaknya framework modern, di mana pembaruan data langsung terlihat pada halaman web secara otomatis tanpa manual re-rendering.

Konsep Integrasi Reactive System dan DOM Renderer

Integrasi ini bekerja dengan cara menghubungkan dua hal penting:

  1. State Reactive, yaitu data yang dipantau perubahan nilainya (menggunakan Proxy dan Reflect).

  2. DOM Renderer, yaitu fungsi yang bertugas menampilkan data ke halaman dan memperbaruinya ketika state berubah.

Ketika sebuah data diubah, dependency tracking system memicu proses re-render hanya pada elemen DOM yang terkait. Pendekatan ini sangat efisien karena tidak perlu menggambar ulang seluruh halaman — cukup bagian yang relevan saja.

Struktur Dasar Reactive Renderer

Berikut struktur umum sistem reaktif dengan renderer DOM otomatis:

const targetMap = new WeakMap();
let activeEffect = null;

function effect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

function track(target, key) {
  let depsMap = targetMap.get(target);
  if (!depsMap) targetMap.set(target, (depsMap = new Map()));
  let dep = depsMap.get(key);
  if (!dep) depsMap.set(key, (dep = new Set()));
  if (activeEffect) dep.add(activeEffect);
}

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const dep = depsMap.get(key);
  if (dep) dep.forEach(effect => effect());
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      track(target, key);
      return typeof result === "object" ? reactive(result) : result;
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      trigger(target, key);
      return result;
    }
  });
}

Kode di atas membentuk fondasi reaktivitas, lengkap dengan pelacakan dependensi (dependency tracking). Selanjutnya, kita akan menghubungkannya ke DOM Renderer.

Membuat Renderer Otomatis untuk DOM

Kita bisa membuat sistem auto renderer sederhana seperti ini:

function mountApp(root, renderFn) {
  effect(() => {
    root.innerHTML = "";
    root.appendChild(renderFn());
  });
}

Fungsi mountApp() akan secara otomatis merender ulang UI setiap kali data reaktif berubah, berkat sistem effect() yang sudah kita buat.

Contoh penerapan:

<div id="app"></div>
<button id="btn">Tambah</button>

<script>
const state = reactive({ count: 0 });

function render() {
  const container = document.createElement("div");
  container.textContent = `Nilai Count: ${state.count}`;
  return container;
}

const root = document.getElementById("app");
mountApp(root, render);

document.getElementById("btn").addEventListener("click", () => {
  state.count++;
});
</script>

Sekarang setiap kali tombol diklik, nilai state.count akan naik dan tampilan otomatis diperbarui tanpa kita memanggil fungsi render() secara manual.

Optimasi: Virtual DOM Sederhana

Agar sistem lebih efisien, kita dapat menambahkan lapisan Virtual DOM. Dengan cara ini, hanya bagian yang berubah yang akan diperbarui, bukan seluruh elemen.

Contoh konsep sederhananya:

function diff(oldNode, newNode) {
  if (oldNode.textContent !== newNode.textContent) {
    oldNode.textContent = newNode.textContent;
  }
}

Lalu ubah fungsi mountApp menjadi:

function mountApp(root, renderFn) {
  let oldNode = renderFn();
  root.appendChild(oldNode);

  effect(() => {
    const newNode = renderFn();
    diff(oldNode, newNode);
    oldNode = newNode;
  });
}

Dengan sistem ini, hanya teks atau atribut yang berubah yang akan diperbarui, bukan seluruh elemen DOM.

Studi Kasus: Aplikasi Counter Reaktif

Berikut contoh implementasi lengkap dari Integrasi Reactive System dengan DOM Renderer (Auto Update UI):

<div id="app"></div>
<button id="increment">Tambah</button>
<button id="decrement">Kurangi</button>

<script>
  // Reactive core
  const targetMap = new WeakMap();
  let activeEffect = null;

  function effect(fn) {
    activeEffect = fn;
    fn();
    activeEffect = null;
  }

  function track(target, key) {
    let depsMap = targetMap.get(target);
    if (!depsMap) targetMap.set(target, (depsMap = new Map()));
    let dep = depsMap.get(key);
    if (!dep) depsMap.set(key, (dep = new Set()));
    if (activeEffect) dep.add(activeEffect);
  }

  function trigger(target, key) {
    const depsMap = targetMap.get(target);
    if (!depsMap) return;
    const dep = depsMap.get(key);
    if (dep) dep.forEach(effect => effect());
  }

  function reactive(obj) {
    return new Proxy(obj, {
      get(target, key, receiver) {
        const result = Reflect.get(target, key, receiver);
        track(target, key);
        return typeof result === "object" ? reactive(result) : result;
      },
      set(target, key, value, receiver) {
        const result = Reflect.set(target, key, value, receiver);
        trigger(target, key);
        return result;
      }
    });
  }

  // Renderer
  function mountApp(root, renderFn) {
    effect(() => {
      root.innerHTML = "";
      root.appendChild(renderFn());
    });
  }

  // State dan UI
  const state = reactive({ count: 0 });

  function render() {
    const el = document.createElement("div");
    el.textContent = `Jumlah: ${state.count}`;
    return el;
  }

  const root = document.getElementById("app");
  mountApp(root, render);

  document.getElementById("increment").onclick = () => state.count++;
  document.getElementById("decrement").onclick = () => state.count--;
</script>

Ketika pengguna menekan tombol, nilai count akan berubah dan tampilan otomatis diperbarui tanpa perlu menulis ulang fungsi render().

Keuntungan Sistem Reactive DOM Renderer

  1. Pembaruan Otomatis: Tampilan selalu sinkron dengan data tanpa render manual.

  2. Struktur Modular: State dan UI dipisahkan secara jelas.

  3. Mudah Dikembangkan: Bisa diperluas ke konsep Virtual DOM, template binding, dan component system.

  4. Performa Lebih Baik: Hanya elemen yang terpengaruh yang di-render ulang.

Langkah Pengembangan Selanjutnya

Jika kamu ingin melangkah lebih jauh, sistem ini bisa dikembangkan menjadi:

  • Mini Vue-like Framework, lengkap dengan computed, watch, dan template parser.

  • Component System Modular, seperti App(), Button(), Counter().

  • Binding Dua Arah (Two-Way Binding) untuk input form.

  • Partial Rendering agar hanya bagian tertentu dari UI yang diperbarui.

Kesimpulan

Dengan memahami integrasi reactive system dengan DOM renderer, kita bisa membangun UI JavaScript modern yang benar-benar dinamis tanpa framework besar. Kombinasi antara Proxy, Reflect, dan dependency tracking menciptakan sistem cerdas yang secara otomatis memperbarui tampilan ketika data berubah.

Pendekatan ini tidak hanya melatih logika pemrograman reaktif, tapi juga memberi pemahaman mendalam tentang bagaimana framework populer seperti Vue dan React bekerja di balik layar — dimulai dari JavaScript murni yang sederhana dan efisien.