Computed<T> constructor

Computed<T>(
  1. List<Reactive> _sources,
  2. T _compute(
    1. List<Reactive>
    ), {
  3. List<FluxivityMiddleware<T>>? middlewares,
})

Constructs a new Computed instance that derives its value from reactive sources.

The Computed instance automatically tracks changes to its sources and recalculates its value whenever any source changes. The compute function is responsible for transforming the source values into the computed value.

Parameters:

  • sources: A list of Reactive instances that this computed value depends on. The computed value will update whenever any of these sources change.
  • compute: A function that takes the list of source Reactive instances and returns the computed value. This function should be pure and deterministic (always return the same output for the same inputs).
  • middlewares: Optional list of FluxivityMiddleware instances that can intercept and modify the behavior of this computed value.

Example:

final firstName = Reactive('John');
final lastName = Reactive('Doe');

// Create a computed full name that depends on both first and last name
final fullName = Computed(
  [firstName, lastName],
  (sources) {
    final first = sources[0].value as String;
    final last = sources[1].value as String;
    return '$first $last';
  },
);

print(fullName.value); // "John Doe"

// When a source changes, the computed value updates automatically
firstName.value = 'Jane';
print(fullName.value); // "Jane Doe"

Implementation

Computed(this._sources, this._compute,
    {List<FluxivityMiddleware<T>>? middlewares})
    : _middlewares = middlewares ?? [] {
  _value = _compute(_sources);
  _controller.add(Snapshot(_value, _value));

  _subscription =
      StreamGroup.merge(_sources.map((source) => source.stream)).listen((_) {
    final T newValue;
    try {
      newValue = _compute(_sources);
    } catch (e, st) {
      for (var middleware in _middlewares) {
        middleware.onError(e, st);
      }
      return;
    }
    final oldValue = _value;
    if (newValue != _value) {
      for (var middleware in _middlewares) {
        middleware.beforeUpdate(oldValue, newValue);
      }
      _value = newValue;
      for (var middleware in _middlewares) {
        middleware.afterUpdate(oldValue, newValue);
      }
      bool shouldPublish = _middlewares
          .map((e) => e.shouldEmit(oldValue, newValue))
          .fold(true, (value, element) => value && element);

      if (shouldPublish) {
        Snapshot<T> snapshot = Snapshot(oldValue, newValue);
        if (_isBatchUpdate) {
          _bufferedEvents.add(snapshot);
        } else {
          _controller.add(snapshot);
        }
      }
    }
  });
}