State and Async Work

Choose between signals, memos, session keys, resources, actions, and URL params without breaking the session-loop model.

#signals#resources#actions#async

State and Async Work

In Vango, the important question is not just what state you need. It is where that state belongs and which boundary owns the work.

Local signals

Allocate local reactive state with setup.Signal.

go
1count := setup.Signal(&s, 0)

Common operations:

  • Get()

  • Peek()

  • Set(v)

  • Update(fn)

  • numeric helpers like Inc() and Dec()

  • container helpers like Append(...) , RemoveAt(...) , and UpdateAt(...)

Prefer copy-on-write semantics over in-place mutation.

Shared signals, global signals, and session keys

Use the smallest durable primitive that matches the ownership model.

setup.SharedSignal

Session-scoped, shared, and persisted when a store is configured.

Use it for:

  • cart IDs

  • cross-page filters

  • small per-session preferences

setup.GlobalSignal

App-scoped, shared across sessions, and persisted in the global store.

Use it sparingly for things like announcements or system-wide feature flags.

vango.SessionKey[T]

Typed session-scoped persisted key-value storage. Use it when durable state does not naturally belong to a single component allocation site.

Memos

Use setup.Memo for derived state only.

go
1total := setup.Memo(&s, func() int {
2	sum := 0
3	for _, item := range cart.Get() {
4		sum += item.Qty
5	}
6	return sum
7})

Memos never persist. They derive from reads inside the compute function.

Resources for async reads

Use setup.Resource or setup.ResourceKeyed for blocking reads.

go
1profile := setup.Resource(&s, func(ctx context.Context) (*Profile, error) {
2	return loadProfile(ctx)
3})
go
1user := setup.ResourceKeyed(&s,
2	func() int { return props.Get().UserID },
3	func(ctx context.Context, id int) (*User, error) {
4		return loadUser(ctx, id)
5	},
6)

Rules:

  • loaders may block

  • loaders must honor context.Context

  • loaders must not write reactive state directly

  • keyed resources should be used whenever props, URL state, or local state determine the selection

Render Resource state explicitly with Match(...).

Actions for async mutations

Use setup.Action for async writes triggered from the session loop.

go
1save := setup.Action(&s,
2	func(ctx context.Context, in SaveInput) (*SavedProfile, error) {
3		return saveProfile(ctx, in)
4	},
5	vango.DropWhileRunning(),
6)

Call Run(...) from an event handler or other session-loop code.

Choose a concurrency policy deliberately:

  • CancelLatest() for search-like work

  • DropWhileRunning() for save or delete actions

  • Queue(n) for ordered background work

If work must outlive the session, use github.com/vango-go/vango-jobs instead of treating jobs as slow Actions.

Transactions

Use vango.Tx(...) or vango.TxNamed(...) when one user action updates multiple pieces of state that should commit together.

That gives you clearer intent, fewer intermediate rerenders, and atomicity for persisted writes.

URL params

Use setup.URLParam for shareable state that belongs in the URL.

go
1query := setup.URLParam(&s, "q", "", vango.Replace, vango.URLDebounce(300*time.Millisecond))
2page := setup.URLParam(&s, "page", 1, vango.Replace)

Use Replace for filters and search fields. Use Push when each change should become a real navigation step.

Concurrency rules

Do not start manual goroutines in:

  • Setup

  • render

  • event handlers

  • OnMount

  • Effect

  • OnChange

Do not do blocking work on the session loop.

If you need async reads, use Resources. If you need async writes, use Actions. If you need a route-bound POST contract, use a page Action plus setup.RouteForm.

Choosing the right primitive

local reactive statesetup.Signal
derived statesetup.Memo
async readsetup.Resource / setup.ResourceKeyed
live async mutationsetup.Action
route-bound form postpage Action + setup.RouteForm
session durable KVvango.SessionKey[T]
session-shared persisted statesetup.SharedSignal
app-global persisted statesetup.GlobalSignal
shareable URL statesetup.URLParam

Next, read Building Views with el for the rendering side of the framework.