Hooks

Vortex relies on side effects, and as such it would fall under the Dysfunctional Programming paradigm.

What are Hooks?

Hooks are what Vortex uses to make your code reactive. In essence, hooks are simply functions that fire based on change events. These events are tied to variables. As an example, the onChange hook fires whenever a variable that implements the hook changes (essentially whenever <var> = <value> is encountered in the interpreter).

var x = 0
var y = 0

x::onChange((data) => {
    y = data.current * 2
})

x = 10
println(y) // 20

The implementation of standard hooks has the following syntax:

<varName>::<hookName>((data) => {...}, name: optional)

Note the data parameter that we exposed in the above hook. Each hook exposes data that the function can use when it fires. The data object contains three properties:

  • old: The old value

  • current: The new value

  • name: Name of the variable OR custom name if provided

Hooks are a great way implement reactivity in your code. However, hooks do make your code more complex and hard to track if they are overused. Make sure that you structure your code in a way that exposes the hooks and makes them front and centre, so that they can be more easily maintained.

Hook Recursion

It's possible for hooks to be self-referential, however there are measures in place that protect you from infinitely recursing.

Consider this scenario:

var x = 0

x::onChange((e) => {
    e.current += 10
})

x = 5

println(x) // Output: 15

It's easy to assume that since x (via e.current) is changing inside its own hook, it's going to fire the same hook again and again until we hit a recursion error.

However that is not the case. The values that are exposed inside the hook (old and current) have their own hooks temporarily removed until the hook function has finished evaluation. This means that even if the value changes inside its own hook, no new functions are fired. The hooks are added back to the value once execution is complete, allowing it to be fired off again.

Note also that we didn't use the value x directly here. There's a subtle reason for this.

var x = 0

x::onChange((e) => {
    x += 10
})

x = 5

println(x) // Output: 5

Let's walk through what's happening here.

Firstly, x is being set to 5.

This triggers the hook, which injects the object e with these values: { old: 0, current: 5 }

Then, x is being set to 15.

Once the hook has finished execution, it sets the value of x to be e.current, hence 5.

In the example prior to this, we set e.current instead, which then became the new value of x.

Chaining Hooks

Hooks can call other hooks in a chain reaction manner:

var x = 0
var y = 0
var z = 0

x::onChange((e) => {
    y += e.current + 3
})

y::onChange((e) => {
    z = e.current / 2
})

x = 5

println(x) // 5
println(y) // 8
println(z) // 4

Here's the flow of the above example:

x = 5 -> y = 5 + 3 -> z = (5 + 3) / 2

Last updated