::Site::::Page::

# Understanding Refs

Refs lend themselves extremely well to reactive GUI programming.

This guide explains how they work and shows some basic recipes.

# Basics

A ref is like a pointer that has .val and .set(val)

import { $, print } from '/api.js'

const r = $(0)
print('val is', r.val)  // val is 0

r.set(r.val + 1)
print('val is', r.val)  // val is 1

You can use the convenience getter/setter value

import { $, print } from '/api.js'

const r = $(0)
print('val is', r.value)  // val is 0

r.value++
print('val is', r.value)  // val is 1

You can .watch() it.

import { $, print } from '/api.js'

const r = $(0)
r.watch(n => print('val is', n))

r.value = 3  // val is 3
r.value++    // val is 4

You can .adapt() it to another value.

import { $, print } from '/api.js'

const r = $(0)
const r2 = r.adapt(n => n * 2)
r2.watch(n => print('val is', n))

r.value = 3  // val is 6
r.value++    // val is 8

# Properties

You can back existing properties with a ref.

import { $, print, makeRef } from '/api.js'

const point = { x: 0, y: 0 }
const $x = makeRef(point, 'x')

$x.watch(x => print('val is', x))

point.x = 10  // val is 10
point.x++     // val is 11

You can even back class properties.

import { $, print, makeRef } from '/api.js'

class Point {
  x = 0
  y = 0

  $x = makeRef(this, 'x')
  $y = makeRef(this, 'y')
}

const point = new Point()

point.$x.watch(x => print('val is', x))

point.x = 10 // val is 10
point.x++    // val is 11

In fact, all built-in view classes do this to back all their properties.

# Advanced

You can create a new ref based on multiple other refs:

import { $, print, multiplex } from '/api.js'

const r1 = $(1)
const r2 = $(100)

const r3 = multiplex([r1, r2], (v1, v2) => v1 * v2)
print(r3.val) // 100

r3.watch(n => print(`${r1.val} * ${r2.val} = ${n}`))

r1.value++    // 2 * 100 = 200
r1.value++    // 3 * 100 = 300
r2.value *= 2 // 3 * 200 = 600

You can change a value before watchers see it.

This is useful for normalizing/constraining values.

import { $, print } from '/api.js'

const r = $(0)
r.watch(n => print('val is', n))
r.intercept(n => Math.max(0, Math.min(10, n)))

r.value = 9  // val is 9
r.value++    // val is 10
r.value++    // (nothing printed; no change in value)

print('val is currently', r.val) // val is currently 10

You can make one ref defer to another.

This basically ties the two refs together.

import { $, print } from '/api.js'

const first = $(10)
first.watch(n => print('first is', n))

print(first.val) // 10

const second = $(0)

first.defer(second)  // first is 0
first.value++        // first is 1
second.value++       // first is 2

print(first.val)  // 2
print(second.val) // 2

second.watch(n => print('second is', n))

first.value++   // first is 3
                // second is 3

second.value++  // first is 4
                // second is 4