Hooks

Let's build out a simple Color structure that can validate and restrict its own values using hooks.

Hooks are useful when you want to handle certain events under the hood without letting the user of your library worry about them.

In this example, we want to create a Color type that can hold red, green and blue values. But we want to ensure that these values are never negative or exceed 255.

How do we do this so that the users of our Color type don't have to worry about it, or have to call specific setter functions that cap the values manually?

Hooks To The Rescue

Let's define our Color struct:

color.vtx
type Color = (r = 0, g = 0, b = 0) => {
    const cap = (num, low, high) => {
        if (num > high) {
            return high
        } else if (num < low) {
            return low
        }
        return num
    }

    r = cap(r, 0, 255)
    g = cap(g, 0, 255)
    b = cap(b, 0, 255)

    r::onChange((info) => {
        println("Capping 'r'")
        info.current = cap(info.current, 0, 255)
    })

    g::onChange((info) => {
        println("Capping 'g'")
        info.current = cap(info.current, 0, 255)
    })

    b::onChange((info) => {
        println("Capping 'b'")
        info.current = cap(info.current, 0, 255)
    })

    return { r: r, g: g, b: b }
}

Note that unlike our previous example, we didn't simply return an object. We had to do some housekeeping first.

Because we want to make sure that the values passed into the constructor are within the 0-255 limits, we cap them on construction using the cap function we just defined.

After that, we apply an onChange hook to each value. This hook simply fires whenever the value changes, and caps it to the defined limits.

Next, we construct an object from those values and return it.

What we've done now is ensured that whenever a new Color object is instantiated, the values are capped, and whenever those values change, they adhere to the limit.

This means that the user will never have to worry about making sure the values they pass in or modify are within the correct range, nor do they have to use a setter function to set those values. The hooks we defined take care of that logic under the hood.

Let's now use our new Color type:

stack.vtx
var color = Color(100, 200, 234)

color.r += 400
color.g -= 300

println(color)

/*
    Capping 'r'
    Capping 'g'
    { r: 255, g: 0, b: 234 }
*/

As you can see, we were able to modify the color values directly without having to worry about capping them manually.

Hooks are indeed a great way to implement under the hood functionality that abstracts complex or burdensome logic away from the user. Howerver, be careful not to overuse them as they can create complexity instead of reducing it.

Last updated