-
Notifications
You must be signed in to change notification settings - Fork 23
Add Action0 and Action1 that will be able to be made null-safe #190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,11 +20,30 @@ import 'package:w_common/disposable.dart'; | |
|
|
||
| import 'package:w_flux/src/constants.dart' show v3Deprecation; | ||
|
|
||
| /// Like [Action1], but without an expected payload (aka a void payload). | ||
| class Action0 extends Action1<void> { | ||
| @override | ||
| String get disposableTypeName => 'Action1'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be 'Action0' |
||
|
|
||
| @override | ||
| Future call([void payload]) => super.call(null); | ||
| } | ||
|
|
||
| /// Like [Action1], but payloads cannot be made non-nullable since the argument | ||
| /// to [call] is optional. | ||
| class Action<T> extends Action1<T> { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thought is that we'd either:
|
||
| @override | ||
| String get disposableTypeName => 'Action'; | ||
|
|
||
| @override | ||
| Future call([T payload]) => super.call(payload); | ||
| } | ||
|
|
||
| /// A command that can be dispatched and listened to. | ||
| /// | ||
| /// An [Action] manages a collection of listeners and the manner of | ||
| /// An [Action1] manages a collection of listeners and the manner of | ||
| /// their invocation. It *does not* rely on [Stream] for managing listeners. By | ||
| /// managing its own listeners, an [Action] can track a [Future] that completes | ||
| /// managing its own listeners, an [Action1] can track a [Future] that completes | ||
| /// when all registered listeners have completed. This allows consumers to use | ||
| /// `await` to wait for an action to finish processing. | ||
| /// | ||
|
|
@@ -45,15 +64,15 @@ import 'package:w_flux/src/constants.dart' show v3Deprecation; | |
| /// when a consumer needs to check state changes immediately after invoking an | ||
| /// action. | ||
| /// | ||
| class Action<T> extends Object with Disposable implements Function { | ||
| class Action1<T> extends Object with Disposable implements Function { | ||
| @override | ||
| String get disposableTypeName => 'Action'; | ||
| String get disposableTypeName => 'Action1'; | ||
|
|
||
| List _listeners = []; | ||
| List<_ActionListener<T>> _listeners = []; | ||
|
|
||
| /// Dispatch this [Action] to all listeners. If a payload is supplied, it will | ||
| /// be passed to each listener's callback, otherwise null will be passed. | ||
| Future call([T payload]) { | ||
| /// Dispatch this [Action1] to all listeners. The non-nullable payload will | ||
| /// be passed to each listener's callback. | ||
| Future call(T payload) { | ||
| // Invoke all listeners in a microtask to enable waiting on futures. The | ||
| // microtask queue is emptied before the event loop continues. This ensures | ||
| // synchronous listeners are invoked in the current tick of the event loop | ||
|
|
@@ -65,23 +84,24 @@ class Action<T> extends Object with Disposable implements Function { | |
| // a [Stream]-based action implementation. At smaller sample sizes this | ||
| // implementation slows down in comparison, yielding average times of 0.1 ms | ||
| // for stream-based actions vs. 0.14 ms for this action implementation. | ||
| Future callListenerInMicrotask(l) => Future.microtask(() => l(payload)); | ||
| Future callListenerInMicrotask(_ActionListener<T> l) => | ||
| Future.microtask(() => l(payload)); | ||
| return Future.wait(_listeners.map(callListenerInMicrotask)); | ||
| } | ||
|
|
||
| /// Cancel all subscriptions that exist on this [Action] as a result of | ||
| /// Cancel all subscriptions that exist on this [Action1] as a result of | ||
| /// [listen] being called. Useful when tearing down a flux cycle in some | ||
| /// module or unit test. | ||
| @Deprecated('Use (and await) dispose() instead. $v3Deprecation') | ||
| void clearListeners() { | ||
| _listeners.clear(); | ||
| } | ||
|
|
||
| /// Supply a callback that will be called any time this [Action] is | ||
| /// Supply a callback that will be called any time this [Action1] is | ||
| /// dispatched. A payload of type [T] will be passed to the callback if | ||
| /// supplied at dispatch time, otherwise null will be passed. Returns an | ||
| /// [ActionSubscription] which provides means to cancel the subscription. | ||
| ActionSubscription listen(dynamic onData(T event)) { | ||
| ActionSubscription listen(dynamic Function(T event) onData) { | ||
| _listeners.add(onData); | ||
| return ActionSubscription(() => _listeners.remove(onData)); | ||
| } | ||
|
|
@@ -97,13 +117,15 @@ class Action<T> extends Object with Disposable implements Function { | |
| } | ||
| } | ||
|
|
||
| /// A subscription used to cancel registered listeners to an [Action]. | ||
| typedef _ActionListener<T> = dynamic Function(T event); | ||
|
|
||
| /// A subscription used to cancel listeners of an [Action0] or [Action1]. | ||
| class ActionSubscription { | ||
| Function _onCancel; | ||
|
|
||
| ActionSubscription(this._onCancel); | ||
|
|
||
| /// Cancel this subscription to an [Action] | ||
| /// Cancel this subscription to an [Action], [Action0] or [Action1]. | ||
| void cancel() { | ||
| if (_onCancel != null) { | ||
| _onCancel(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -153,16 +153,31 @@ class Store extends Stream<Store> with Disposable { | |
| /// called until that future has resolved. | ||
| /// | ||
| /// If the `Store` has been disposed, this method throws a [StateError]. | ||
| void triggerOnActionV2<T>(Action<T> action, | ||
| [FutureOr<dynamic> onAction(T payload)]) { | ||
| if (isOrWillBeDisposed) { | ||
| throw StateError('Store of type $runtimeType has been disposed'); | ||
| } | ||
| @deprecated | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was there a reason this was deprecated and triggerOnAction1 added, other than cleanliness and improved naming consistency? Those seem like good enough reasons, but I was curious if there was some other factor
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope that was it |
||
| void triggerOnActionV2<T>(Action1<T> action, | ||
| [FutureOr<dynamic> onAction(T payload)]) => | ||
| triggerOnAction1(action, onAction); | ||
|
|
||
| void triggerOnAction0(Action0 action, [FutureOr<void> onAction()]) { | ||
| _assertNotDisposed(); | ||
| manageActionSubscription(action.listen((payload) async { | ||
| if (onAction != null) { | ||
| await onAction(payload); | ||
| } | ||
| await onAction?.call(); | ||
| trigger(); | ||
| })); | ||
| } | ||
|
|
||
| void triggerOnAction1<T>(Action1<T> action, | ||
| [FutureOr<void> onAction(T payload)]) { | ||
| _assertNotDisposed(); | ||
| manageActionSubscription(action.listen((payload) async { | ||
| await onAction?.call(payload); | ||
| trigger(); | ||
| })); | ||
| } | ||
|
|
||
| void _assertNotDisposed() { | ||
| if (isOrWillBeDisposed) { | ||
| throw StateError('Store of type $runtimeType has been disposed'); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to consider making this
extends Action<Null>instead, to help prevent consumers from accidentally passing in values without knowing they'll be discarded.Example: https://dartpad.dev/?id=97af2f0dd8c7919807bd6864ab085794
The relevant pieces from that ^