signal<S> function

Signal<S> signal<S>(
  1. S value, {
  2. bool forceUpdates = false,
  3. EffectMutation effectMutation = EffectMutation.yes,
})

Mark a value as reactive.

Mark the given value as reactive to explicitly allow it to be observed by effects. This returns a Record of type Signal which exposes an Accessor that returns the value itself and a Setter that reactively updates the state and any side effects.

Implementation

Signal<S> signal<S>(S value,
    {bool forceUpdates = false,
    EffectMutation effectMutation = EffectMutation.yes}) {
  final state = BehaviorSubject<S>.seeded(value);
  final Set<Function()> dependents = {};

  S getState() {
    final effect = owner;

    if (effect != null) dependents.add(effect(state));

    return state.value;
  }

  void setState(S value) {
    switch (effectMutation) {
      // Allow this state to be written in an effect. This is the default
      // setting as [signal] assumes that the user is responsible enough to
      // avoid infinite loops by mutating data the effect is dependent on.
      case EffectMutation.yes:
        null;
        break;

      // Prevent this state to be written to in an effect. Select this option if
      // it is safe to assume continued operation when an attempt to write to
      // this state is made by ignoring any writes to it in an effect.
      case EffectMutation.no:
        final effect = owner;
        if (effect != null) return;

      // Prevent this state to be written to in an effect. Select this option if
      // it is unsafe to assume continued operation when this state is written
      // to in an effect.
      case EffectMutation.error:
        // TODO: Dedicated error type for writing to effects.
        final effect = owner;
        if (effect != null) throw EffectWriteError(effect);

      // The options .no and .error are typically selected as a way to prevent
      // an infinite loop from occuring.
    }

    switch (forceUpdates) {
      case true:
        state.add(value);
      case false:
        (value == state.value) ? null : state.add(value);
    }

    // ignore: avoid_function_literals_in_foreach_calls
    dependents.forEach((sideEffect) => sideEffect());
  }

  return (getState, setState);
}