lily/store
In Lily, the Store holds your main application state and update
logic, sitting across both the client and the server. The store works by
having a model and an associated update logic (that takes the model and a
message and returns a new model). The mental model for the data within the
store is similar to the TEA/MVU model that Elm/Lustre uses without the
actual rendering/view part, which is owned by the individual components.
Model fields that should not be synced to the server can be wrapped in
Local. These fields are client-only: the server holds them at
their initial values, and the client runtime preserves them when the server
sends a snapshot on reconnect. Pair with
client.session_field to persist them
across page navigations.
The same store runs on the client via
client.start and the server via
server.start, meaning your update function
works identically on both sides.
Here’s a very basic example of the idea behind how the store works.
import lily/store
pub type Model {
Model(count: Int, user: String)
}
pub type Message {
Increment
Decrement
SetUser(String)
}
pub fn update(model: Model, message: Message) -> Model {
case message {
Increment -> Model(..model, count: model.count + 1)
Decrement -> Model(..model, count: model.count - 1)
SetUser(name) -> Model(..model, user: name)
}
}
pub fn main() {
let app_store = store.new(Model(count: 0, user: "Guest"), with: update)
// Pass to client.start() or server.start()
}
Internally, messages are processed sequentially within the client runtime,
which batches multiple apply calls between render frames, so rapid
bursts of messages don’t lead to unnecessary computation of what should be
rendered much faster than the DOM is able to update.
Types
Model fields that are client-only and not synced to the server should be
wrapped using Local(_). The server holds Local fields at their initial
values and the client runtime preserves them when applying a server
snapshot on reconnect.
pub type Model {
Model(count: Int, theme: store.Local(String))
}
pub type Local(a) {
Local(a)
}
Constructors
-
Local(a)
The store with your application state and update logic. The same store
runs on both the client (via client.start)
and the server (via server.start).
pub type Store(model, message) {
Store(model: model, update: fn(model, message) -> model)
}
Constructors
-
Store(model: model, update: fn(model, message) -> model)
Values
pub fn new(
initial_model model: model,
with update: fn(model, message) -> model,
) -> Store(model, message)
Create a new store, seeded with an initial_model (similar to Lustre’s
init) and an update function that transforms the model based on a given
message. This is essentially a wrapper around the Store
constructor that reads slightly nicer.
let app_store =
Model(count: 0, user: "Guest")
|> store.new(with: update)