Skip to main content
Version: 1.0.0

State and ownership

Import using Pine;. P.Source<T> creates writable state; P.Derive caches tracked calculations; P.Effect observes changes. P.Batch groups writes and P.Untrack reads without subscriptions. Sources can exist independently; calculations, effects and cleanup require an owned scope.

P.Root creates an explicit scope. P.Mount owns a native tree. Scope.Run enters a live scope; disposal attempts all cleanup in reverse order. P.Cleanup owns callbacks, disposables and native Unity objects. Context providers remain visible to deferred views and their event callbacks. See state, scope and signal references.

Choose a state operation​

OperationUse it for
P.SourceWritable state changed by application code or UI callbacks.
P.DeriveA cached calculation that follows its tracked source reads.
P.EffectOwned work that must run when its tracked dependencies change.
P.BatchRelated writes that should notify observers together.
P.UntrackReading state without adding that read as a dependency.

Create owned calculations inside App.Mount, a mounted component renderer or an explicit P.Root. A source may outlive one interface, but its observers need a live owner so they can stop when the interface disappears. Use Pine's Unity UI operations on the Unity main thread.

Bind a calculation to a native control​

This fragment belongs inside an owned renderer such as App.Mount:

var count = P.Source(0);
var enabled = P.Derive(() => count.Value > 0);
return P.Vertical(
P.Text(() => $"Count: {count.Value}"),
P.Button("Reset", onClick: () => count.Value = 0, interactable: enabled)
);

The getter and derived calculation track count.Value. A write updates the retained native components. Passing a literal value instead reads only once; see the complete counter tutorial for startup and layout sizing.

Diagnose a missing update​

Check that the binding reads the source inside a getter, rather than passing an already calculated literal. If you mutate an object held by a source in place, use Source.Notify() to notify observers explicitly; assigning an equal value can be suppressed by the source's equality comparer. If an observer has stopped, check whether its owning scope was disposed. The signal reference documents tracked reads, untracked Peek() and notification behaviour.