blob: 5ba9468fb4e0989751466eecf54d006359a59941 [file] [log] [blame]
part of '../core.dart';
abstract class Reaction implements Derivation {
bool get isDisposed;
void dispose();
void _run();
}
class ReactionImpl implements Reaction {
ReactionImpl(this._context, Function() onInvalidate,
{required this.name, void Function(Object, Reaction)? onError}) {
_onInvalidate = onInvalidate;
_onError = onError;
}
void Function(Object, ReactionImpl)? _onError;
final ReactiveContext _context;
late void Function() _onInvalidate;
bool _isScheduled = false;
bool _isDisposed = false;
bool _isRunning = false;
@override
final String name;
@override
Set<Atom>? _newObservables;
@override
// ignore: prefer_final_fields
Set<Atom> _observables = {};
bool get hasObservables => _observables.isNotEmpty;
@override
// ignore: prefer_final_fields
DerivationState _dependenciesState = DerivationState.notTracking;
@override
MobXCaughtException? _errorValue;
@override
MobXCaughtException? get errorValue => _errorValue;
@override
bool get isDisposed => _isDisposed;
@override
void _onBecomeStale() {
schedule();
}
Derivation? startTracking() {
_context.startBatch();
_isRunning = true;
return _context._startTracking(this);
}
void endTracking(Derivation? previous) {
_context._endTracking(this, previous);
_isRunning = false;
if (_isDisposed) {
_context._clearObservables(this);
}
_context.endBatch();
}
void track(void Function() fn) {
_context.startBatch();
final notify = _context.isSpyEnabled;
DateTime? startTime;
if (notify) {
startTime = DateTime.now();
_context.spyReport(ReactionSpyEvent(name: name));
}
_isRunning = true;
_context.trackDerivation(this, fn);
_isRunning = false;
if (_isDisposed) {
_context._clearObservables(this);
}
if (_context._hasCaughtException(this)) {
_reportException(_errorValue!);
}
if (notify) {
_context.spyReport(EndedSpyEvent(
type: 'reaction',
name: name,
duration: DateTime.now().difference(startTime!)));
}
_context.endBatch();
}
@override
void _run() {
if (_isDisposed) {
return;
}
_context.startBatch();
_isScheduled = false;
if (_context._shouldCompute(this)) {
try {
_onInvalidate();
} on Object catch (e, s) {
// Note: "on Object" accounts for both Error and Exception
_errorValue = MobXCaughtException(e, stackTrace: s);
_reportException(_errorValue!);
}
}
_context.endBatch();
}
@override
void dispose() {
if (_isDisposed) {
return;
}
_isDisposed = true;
if (_isRunning) {
return;
}
_context.spyReport(ReactionDisposedSpyEvent(name: name));
// ignore: cascade_invocations
_context
..startBatch()
.._clearObservables(this)
..endBatch();
}
void schedule() {
if (_isScheduled) {
return;
}
_isScheduled = true;
_context
..addPendingReaction(this)
..runReactions();
}
@override
// ignore: unused_element
void _suspend() {
// Not applicable right now
}
void _reportException(Object exception) {
if (_onError != null) {
_onError!(exception, this);
return;
}
if (_context.config.disableErrorBoundaries == true) {
// ignore: only_throw_errors
throw exception;
}
if (_context.isSpyEnabled) {
_context.spyReport(ReactionErrorSpyEvent(exception, name: name));
}
_context._notifyReactionErrorHandlers(exception, this);
}
}