sidebar_position: 3
Reactivity
Tama adopts observable values as another primitive.
In the current inflator implementation, the practical runtime requirement is a plain subscribe(...) method.
Beyond that, it treats any object that has either get or set to be an accessor. Which can be combined with observable, getting an Observable Accessor.
It might seem to be a problematic decision considering
MapandSetbuilt-ins, but this is a start to solving it and in practice it's almost never a problem.
There is no built-in state manager (in the library), Tama only handles Observables as is. Now creating data flows is your problem :) Joking, it always was, now it's separated from the "framework", so you can use any library that supports Observables. Like the one Denshya has - Reactive.
const regularText = "Static Value"
const observableText = {
i: 0,
get: () => "Initial",
subscribe(next) {
setInterval(() => next("Next: " + this.i++))
return { unsubscribe() {} }
}
}
Custom State
To implement a state, all you need is this simple snippet.
class State<T> {
private readonly callbacks = new Set<(value: T) => void>()
private value: T
constructor(initialValue: T) { this.value = initialValue }
get(): T { return this.value }
set(value: T): void {
this.value = value
this.dispatch(value)
}
private dispatch(value: T) {
this.callbacks.forEach(callback => callback(value))
}
subscribe(next: (value: T) => void) {
this.callbacks.add(next)
return { unsubscribe: () => void this.callbacks.delete(next) }
}
}
In practice it looks like this
function ProductCard() {
const title = new State("Title")
const image = new State("Image source")
return (
<>
<div className="product-card__title">{title}div>
<img className="product-card__image" src={image} alt="Preview" />
<input value={title} />
<input value={image} />
>
)
}
To get started faster with this, try Reactive state library.
NOTE
If your environment or library already exposes subscribe-compatible event or state sources, Tama can bind them directly.
Dual binding
Some elements may have binding in two directions, which means that [state updates element] and [element updates state].
This is common for user input elements just like input and textarea.
If you're using Reactive library, you can avoid this behavior with readonly method.
function ProductCard() {
const title = new State("Title")
return (
<>
// User inputs won't update the `title`.
<input value={title.readonly()} />
// User inputs WILL update the `title`.
<input value={title} />
>
)
}
Using @denshya/reactive
StateArray
import { State, StateArray } from "@denshya/reactive"
function App() {
const choice = new State(0)
const items = new StateArray(Array(3).fill(0))
setInterval(() => items.set(Array(Math.floor(Math.random() * 15)).fill(0)), 1000)
return (
<div>
{items.map((_, index) => (
<button disabled={choice.is(index)} on={{ click: () => choice.set(index) }}>{index}button>
))}
div>
)
}
WICG Observable (Web Standard)
Tama also supports the WICG Observable proposal as a first-class citizen, so you can use it directly if your environment supports it.
function App() {
const time = new Observable(subscriber => {
const interval = setInterval(() => subscriber.next(new Date()), 1000)
return () => clearInterval(interval)
})
return <div>{time}div>
}