Last active
February 22, 2024 22:51
-
-
Save devhammed/f741fc0324cc7d3ad1061888e675d9b0 to your computer and use it in GitHub Desktop.
120-lines implementation of a reactive system in JavaScript.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { reactive, effect,computed, ref, watch} = require('./reactive'); | |
const x = ref(0) | |
const y = ref(0) | |
const state = reactive({ | |
showSword: false, | |
message: "Hey young padawan!", | |
}); | |
const sword = ref({ | |
sharpness: 10, | |
durability: 100, | |
}); | |
const computedMessage = computed(() => { | |
return state.showSword ? "You have a sword!" : "You have no sword!"; | |
}); | |
const effectToDispose = effect(() => { | |
console.log('tracking showSword but I will be disposed after the second change: ', state.showSword); | |
}); | |
effect(() => { | |
console.log(computedMessage.value); | |
}); | |
effect(() => { | |
console.log(state.message); | |
}); | |
effect(() => { | |
console.log('Sword durability', sword.value.durability); | |
}); | |
watch(() => state.showSword, (newVal, oldVal) => { | |
console.log("showSword changed from " + oldVal + " to " + newVal); | |
}); | |
watch(computedMessage, (newVal, oldVal) => { | |
console.log("Computed message changed from '" + oldVal + "' to '" + newVal + "'"); | |
}); | |
watch(() => sword.value.sharpness, (newVal, oldVal) => { | |
console.log("Sword sharpness changed from " + oldVal + " to " + newVal); | |
}); | |
watch( | |
() => x.value + y.value, | |
(sum) => { | |
console.log(`sum of x + y is: ${sum}`) | |
} | |
) | |
x.value = 1; | |
y.value = 2; | |
state.showSword = true; | |
state.message = "Welcome to the dark side!"; | |
effectToDispose(); | |
state.showSword = false; | |
sword.value = { | |
...sword.value, | |
sharpness: 20, | |
} | |
y.value = 3; | |
x.value = 4; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let activeEffect = null; | |
let disposables = new Set(); | |
let targetMap = new WeakMap(); | |
function track(target, key) { | |
if (activeEffect) { | |
let depsMap = targetMap.get(target); | |
if ( ! depsMap) { | |
targetMap.set(target, depsMap = new Map()); | |
} | |
let deps = depsMap.get(key); | |
if ( ! deps) { | |
depsMap.set(key, deps = new Set()); | |
} | |
deps.add(activeEffect); | |
} | |
} | |
function trigger(target, key) { | |
const depsMap = targetMap.get(target); | |
if ( ! depsMap) { | |
return; | |
} | |
const deps = depsMap.get(key); | |
if ( ! deps) { | |
return; | |
} | |
disposables.forEach(function (fx) { | |
if (deps.has(fx)) { | |
deps.delete(fx); | |
disposables.delete(fx); | |
} | |
}); | |
deps.forEach((fx) => fx()); | |
} | |
function effect(fn) { | |
activeEffect = fn; | |
fn(); | |
activeEffect = null; | |
return () => disposables.add(fn); | |
} | |
function ref(value) { | |
const valueKey = 'value'; | |
const refObject = { | |
get value() { | |
track(refObject, valueKey); | |
return value; | |
}, | |
set value(newValue) { | |
value = newValue; | |
trigger(refObject, valueKey); | |
} | |
} | |
return refObject; | |
} | |
function computed(fn) { | |
let value = null; | |
let runner = () => value = fn(); | |
effect(runner); | |
return { | |
get value() { | |
return runner(); | |
} | |
} | |
} | |
function reactive(obj) { | |
return new Proxy(obj, { | |
get(target, key) { | |
track(target, key); | |
return target[key]; | |
}, | |
set(target, key, value) { | |
target[key] = value; | |
trigger(target, key); | |
} | |
}) | |
} | |
function watch(selector, fn) { | |
let get = () => typeof selector === 'object' && 'value' in selector | |
? selector.value | |
: selector(); | |
let value = get(); | |
effect(() => { | |
let newValue = get(); | |
if (value !== newValue) { | |
fn(newValue, value); | |
value = newValue; | |
} | |
}); | |
} | |
module.exports = { reactive, ref, computed, effect, watch }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment