After completing my basic implementation of render functions, I went further to tap into Reactive Programmming using JavaScript.
import { reactive } from "../lib/index.js"
// App state
const state = reactive({ count: 0 })
// track `count`
watchEffect(() => console.log(`Count: ${state.count}`))
// increment
++state.count
// > Count: 1
++state.count
// > Count: 2
Temperature Converter -> source code -> demo link
This repository is not for beginners; it's targeted at intermediate or professional developers and programmers who want to take their JavaScript skills to the next level.
Some of the basics (like loops, conditionals, and closures) are not discussed at all. If you find you need to brush up on some of those topics, refer to the MDN Web Docs
NB: Intermediate understanding of JavaScript and some of the latest ECMAScript Standards like classes, modules, shortcircuiting, and more.
Modern JavaScript Frameworks have different implementations on gaining reactivity; common usecases include DOM updates and App state.
I recommend that you check out how some modern frameworks implement reactivity; - Vue, - Solid, -Svelte
Warning: No application should be built on top of this Reactivity System as it is intended to be for learning or study purposes only.
My understanding of Reactive JavaScript is based on the idea that when the variable changes, all computations that depend on it get re-evaluated.
Example: Spreadsheets like Microsoft Excel use cell referencing such that if cell A3 is the sum of cells A1 and A2, then whenever cell A1 or A2 changes, the value of cell A3 gets re-evaluated.
- Inorder to gain reactivity, wrap the initial value (primitive type) into an object with a getter and a setter.
- In the getter, every time the value is accessed, we capture the active caller (a subscriber/dependant) to this value, and store it (as a subscriber to this value).
- In the setter, whenever the value is being changed, make the change, and then notify all subscribers/dependants that the value is changed, so re-evaluate (update).
- Incase the initial value was an object, wrap the object in a Proxy with set and get traps which work like getters and setters as described before.
The naming of the building blocks is based on Vue's Reactivity.
-
watchEffect
A function that opens a subcription to reactive values and so saves the action to be notified when updates occur
Takes an
action
as an argumentInterface
watchEffect(action: function)
We can watch all reactive value like
ref
,computed
and properties ofreactive
objectsHow does
watchEffect
work, check it out here -
ref
A function that creates a reactive object from a primitive value either string, number or null
Takes any primitive type and return a object with the
value
propertyInterface
const variable_name = ref(value: string | number | null | undefined)
Example
import { ref } from "../lib/index.js"; // first name const firstName = ref("Brad") // read value console.log(`My first name is ${firstName.value}.`) // watch firstName for changes watchEffect(() => { console.log(`Your first name has changed to ${firstName.value}`) }) // change value firstName.value = "Henry" // change again firstName.value = "John"
How does
ref
work, check it out here -
computed
A function that stores a value that re-calculates when it's dependencies get updated.
This is used in conjuction with other reactive values like
ref
,reactive
Takes a function as an argument and returns an object with only a getter for a
value
propertyInterface
const variable_name = computed(action: function)
Example
import { computed, ref, watchEffect } from "../lib/index.js"; // cells A1 and A2 const A1 = ref(1), A2 = ref(2) // cell A3 - computed sum const A3 = computed(() => A1.value + A2.value) // watch cell A3 for changes if either cell A1 or A2 changes watchEffect(() => { // read A3 console.log(`A3 is ${A3.value}`) }) // change cell A1 ++A1.value // logs > A3 is 4 // change cell A2 A2.value = 5 // logs > A3 is 7
How does
computed
works, check it out here -
reactive
A function that creates a reactive object from only objects not arrays.
Takes an
object
as argument and returns a proxyAccess of properties does not behave like
ref
orcomputed
, they are accessed directly and still reactive.Interface
const variable_name = reactive(obj?: any)
Example
import { reactive } from "../lib/index.js"; //app state const state = reactive({ count: 0 }) // read a property console.log(`Count: ${state.count}`) // logs > Count: 0 // change `count` ++state.count // read a property console.log(`Count: ${state.count}`) // logs > Count: 1
Note: Destructing objects causes a disconnection from reactivity since the properties are referenced in objects and so destruction assignment to a variable de-references the property from the reactive object
How does
reactive
work, check it out here
Source code contains comments that take you through what happens behind the scenes
To build it from scratch as you test each of the building blocks, check out the source code.
I have developed some basic examples to illustrate how reactivity can be useful.
import { ref, computed, watchEffect } from "../lib/index.js"
// count
const count = ref(0)
// double
const double = computed(() => 2 * count)
// watch `count` and `double` for changes
watchEffect(() => {
console.log(`Count: ${count.value}, Double: ${double.value}`)
})
// trigger changes
++count.value
// logs > Count: 1, Double: 2
++count.value
// logs > Count: 2, Double: 4
++count.value
// logs > Count: 3, Double: 6
In case of any issues or inquiries about this, a pull request is welcome.
Thanks.