Dalam pengembangan web modern, reactive system menjadi pondasi bagi framework populer seperti Vue.js, Svelte, dan SolidJS. Konsepnya sederhana: setiap kali data berubah, tampilan secara otomatis menyesuaikan tanpa perlu memanggil fungsi render secara manual. Namun, di balik kesederhanaan itu terdapat sistem yang canggih menggunakan Reflect API dan Dependency Tracking untuk memantau dan memicu pembaruan hanya pada bagian yang benar-benar berubah.
Membangun reactive system dengan Reflect dan dependency tracking memungkinkan pengembang memahami inti dari reaktivitas modern tanpa mengandalkan framework besar. Dengan memahami konsep ini, Anda bisa menciptakan sistem data yang efisien, menghemat performa, dan bahkan merancang mini reactivity engine seperti milik Vue 3 hanya dengan JavaScript murni.
Apa Itu Reflect API dalam JavaScript
Reflect adalah objek bawaan JavaScript yang menyediakan metode untuk mengintervensi operasi dasar pada objek, seperti get, set, deleteProperty, dan lainnya — serupa dengan handler dalam Proxy. Bedanya, Reflect digunakan untuk menjaga perilaku standar objek setelah operasi khusus dilakukan.
Contoh sederhana:
const user = { name: "Arvian" };
const proxy = new Proxy(user, {
get(target, key, receiver) {
console.log(`Mengakses properti: ${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`Mengubah ${key} menjadi ${value}`);
return Reflect.set(target, key, value, receiver);
}
});
console.log(proxy.name);
proxy.name = "Ticiamis";
Hasil:
Mengakses properti: name Mengubah name menjadi Ticiamis
Dengan Reflect, kita bisa memastikan operasi get/set tetap berjalan normal walaupun ditambahkan logika tambahan seperti pelacakan dependensi (dependency tracking).
Mengenal Dependency Tracking
Dependency Tracking adalah konsep yang memungkinkan sistem reaktif mengetahui fungsi mana yang perlu dijalankan ulang ketika sebuah data berubah. Idenya mirip dengan sistem watcher — setiap kali data dibaca oleh fungsi tertentu, sistem mencatat ketergantungan fungsi tersebut terhadap data itu. Ketika data berubah, hanya fungsi-fungsi yang bergantung padanya yang dijalankan kembali.
Membangun Reactive Core Sederhana
Mari kita mulai membuat sistem reaktif sederhana dengan Proxy, Reflect, dan dependency tracking.
1. Membuat Map untuk Menyimpan Dependency
const targetMap = new WeakMap();
function track(target, key) {
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if (!dep) {
dep = new Set();
depsMap.set(key, dep);
}
if (activeEffect) dep.add(activeEffect);
}
track() menyimpan fungsi-fungsi yang bergantung pada properti tertentu ke dalam struktur data WeakMap -> Map -> Set.
2. Menjalankan Fungsi Saat Data Berubah
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) dep.forEach(effect => effect());
}
trigger() dipanggil setiap kali data diubah. Ia akan mencari semua fungsi yang tergantung pada data tersebut dan menjalankannya kembali.
3. Menentukan Fungsi Efek (Reactive Effect)
let activeEffect = null;
function effect(fn) {
activeEffect = fn;
fn(); // jalankan pertama kali
activeEffect = null;
}
Setiap kali effect() dijalankan, fungsi yang dibungkus akan “terdaftar” dalam dependency ketika membaca data reaktif.
4. Membuat Proxy dengan Reflect
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
track(target, key); // catat dependency
return typeof result === "object" ? reactive(result) : result;
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key); // panggil ulang efek
return result;
}
});
}
5. Contoh Implementasi Lengkap
const state = reactive({ count: 0, user: { name: "Arvian" } });
effect(() => {
console.log("Count berubah:", state.count);
});
state.count++; // otomatis memanggil ulang effect()
state.user.name = "Ticiamiz"; // nested reactive
Output:
Count berubah: 0 Count berubah: 1
Setiap kali state.count berubah, efek otomatis dijalankan ulang tanpa perlu re-render manual.
Menambahkan Computed Property
Kita bisa menambahkan kemampuan computed property, yaitu nilai yang bergantung pada reaktivitas lain:
function computed(getter) {
let value;
const runner = () => {
value = getter();
};
effect(runner);
return {
get value() {
return value;
}
};
}
const doubleCount = computed(() => state.count * 2);
console.log(doubleCount.value); // 2x dari state.count
Setiap kali state.count berubah, doubleCount.value otomatis diperbarui.
Keuntungan Pendekatan Reflect & Dependency Tracking
-
Efisien & Terarah: hanya bagian yang terpengaruh data yang dijalankan ulang.
-
Scalable: bisa dikembangkan menjadi reactivity engine seperti Vue 3.
-
Modular & Fleksibel: mudah digabung dengan sistem UI atau framework ringan.
-
Pemahaman Mendalam: membantu memahami cara kerja internal framework modern.
Pengembangan Lebih Lanjut
Setelah sistem dasar ini berjalan, kamu bisa mengembangkannya menjadi:
-
Batch Update System: agar banyak perubahan dieksekusi sekaligus.
-
Watcher System: memantau perubahan properti tertentu saja.
-
Integration Layer: menghubungkannya dengan DOM renderer sederhana.
-
Ref & Reactive Mix: seperti implementasi
ref()di Vue 3.
Dengan tambahan ini, kamu sudah bisa menciptakan mini framework reaktif sendiri berbasis JavaScript murni.
Kesimpulan
Membangun reactive system dengan Reflect dan dependency tracking memberi pemahaman mendalam tentang bagaimana framework modern seperti Vue 3 mengelola reaktivitas secara efisien. Dengan memanfaatkan Proxy, Reflect, dan sistem pelacakan ketergantungan (dep tracking), kamu dapat menciptakan sistem data otomatis yang cepat, ringan, dan fleksibel tanpa menggunakan library eksternal.
Pendekatan ini bukan hanya latihan pemrograman — tapi juga fondasi untuk memahami core concept pengembangan aplikasi reaktif masa depan.