State
Subscriptions And Binding
Subscribe to state, map values, observe changes, and bind state to view setters.
- Subscribe
- SubscribeMap
- SubscribeChanged
- BindTo
Use subscriptions and bindings to connect state values to readers without giving those readers write access.
Subscribing
Basic Subscribe
AzEventToken token = Health.Subscribe(OnHealthChanged);
private void OnHealthChanged(int health)
{
}By default, Subscribe immediately invokes the handler with the current value if the state has one.
Health.Subscribe(OnHealthChanged, notifyImmediately: true);Disable immediate notification when you only care about future changes:
Health.Subscribe(OnHealthChanged, notifyImmediately: false);Token Bag Pattern
using Azkar.Eda;
using UnityEngine;
public sealed class HealthHud : MonoBehaviour
{
[SerializeField] private PlayerViewModel _model;
private readonly AzEventTokenBag _bindings = new();
private void OnEnable()
{
_model.Health.Subscribe(SetHealthText).AddTo(_bindings);
}
private void OnDisable()
{
_bindings.DisposeAll();
}
private void OnDestroy()
{
_model?.Dispose();
}
private void SetHealthText(int health)
{
}
}Subscriber Priority
Subscribers with higher priority run earlier.
Health.Subscribe(ValidateUiState, priority: 100);
Health.Subscribe(UpdateHud, priority: 0);
Health.Subscribe(RecordAnalytics, priority: -10);For equal priorities, subscription order is preserved.
Use priority sparingly. Most state bindings should use the default priority 0.
Weak Subscribers
Pass weak: true to allow Unity-object targets to be removed automatically when destroyed.
Health.Subscribe(OnHealthChanged, weak: true).AddTo(_bindings);This is most useful for instance methods on MonoBehaviour or ScriptableObject targets.
Avoid weak subscriptions with throwaway lambda captures:
// Avoid this pattern with weak: true.
Health.Subscribe(value => UpdateHealth(value), weak: true);Prefer a stable instance method:
Health.Subscribe(OnHealthChanged, weak: true);Even with weak subscriptions, explicit token cleanup is still a good habit.
SubscribeMap
SubscribeMap transforms the state value before passing it to a sink.
Health.SubscribeMap(
hp => hp / 100f,
fill => healthBar.fillAmount = fill).AddTo(_bindings);Common uses:
Score.SubscribeMap(
value => $"Score: {value:N0}",
text => scoreLabel.text = text).AddTo(_bindings);
Muted.SubscribeMap(
muted => muted ? "Muted" : "Sound On",
text => muteLabel.text = text).AddTo(_bindings);
Health.SubscribeMap(
hp => hp < 25,
isCritical => criticalWarning.SetActive(isCritical)).AddTo(_bindings);SubscribeMap supports notifyImmediately, priority, and weak.
Health.SubscribeMap(
hp => Mathf.Clamp01(hp / 100f),
SetFill,
notifyImmediately: true,
priority: 0,
weak: true).AddTo(_bindings);SubscribeChanged
SubscribeChanged receives old and new values.
Health.SubscribeChanged(OnHealthChanged).AddTo(_bindings);
private void OnHealthChanged(int oldHealth, int newHealth)
{
if (newHealth < oldHealth)
{
int damage = oldHealth - newHealth;
Debug.Log($"Took {damage} damage");
}
}Important details:
SubscribeChangeddoes not invoke immediately.- The first callback happens after the first observed change after subscription.
- The old value for that first callback is the state value captured by the wrapper during its first notification.
- If the token is paused, skipped notifications do not update the captured previous value.
Use SubscribeChanged for:
- old/new logging
- threshold crossing
- damage deltas
- undo history
- analytics deltas
Use normal Subscribe or BindTo for UI display.
BindTo
BindTo is one-way binding from state to a setter.
Health.BindTo(SetHealthText).AddTo(_bindings);It is equivalent to subscribing, but the name communicates intent.
Health.BindTo(hp => healthText.text = $"HP: {hp}").AddTo(_bindings);Object Binding Overload
Use the object overload to avoid closure-heavy bindings and make the target explicit.
Health.BindTo(this, static (self, hp) => self.SetHealthText(hp)).AddTo(_bindings);For UI components:
Health.BindTo(healthBar, static (bar, hp) =>
{
bar.fillAmount = Mathf.Clamp01(hp / 100f);
}).AddTo(_bindings);This overload requires a reference-type target.
