Skip to content

Commit 59087e1

Browse files
committed
support nullable action
1 parent 94bba91 commit 59087e1

File tree

4 files changed

+86
-23
lines changed

4 files changed

+86
-23
lines changed

lib/src/redux_store_stream_transformer.dart

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
106106
final currentState = state;
107107

108108
// add initial state
109-
if (identical(type, ActionType.initial)) {
109+
if (identical(wrapper, WrapperAction.initial)) {
110110
final message = '\n'
111111
' ⟶ Action : $type\n'
112112
' ⟹ Current state: $currentState';
113113
_logger?.call(message);
114114
return controller.add(currentState);
115115
}
116116

117-
final action = wrapper.action!;
117+
final action = wrapper.action;
118118
try {
119119
final newState = _reducer(currentState, action);
120120
controller.add(newState);
@@ -151,20 +151,19 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
151151
actionController.stream.listen(onDataActually);
152152

153153
// Add initial action
154-
actionController.add(WrapperAction(null, ActionType.initial));
154+
actionController.add(WrapperAction.initial);
155155

156156
// Listening to upstream actions
157-
final subscriptionUpstream = stream
158-
.map((action) => WrapperAction(action, ActionType.external))
159-
.listen(
160-
actionController.add,
161-
onError: controller.addError,
162-
onDone: controller.close,
163-
);
157+
final subscriptionUpstream =
158+
stream.map((action) => WrapperAction.external(action)).listen(
159+
actionController.add,
160+
onError: controller.addError,
161+
onDone: controller.close,
162+
);
164163

165164
final getState = () => state;
166165
final actionStream = actionController.stream
167-
.map((wrapper) => wrapper.action!)
166+
.map((wrapper) => wrapper.action)
168167
.asBroadcastStream(onCancel: (s) => s.cancel());
169168

170169
subscriptions = [
@@ -220,9 +219,8 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
220219
actions = Stream.error(e, s);
221220
}
222221

223-
final sideEffectAction = ActionType.sideEffect(index);
224222
return actions
225-
.map((action) => WrapperAction(action, sideEffectAction))
223+
.map((action) => WrapperAction.sideEffect(action, index))
226224
.listen(
227225
actionController.add,
228226
onError: stateController.addError,

lib/src/store.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ extension DispatchToExtension<A> on A {
213213
void dispatchTo<S>(RxReduxStore<A, S> store) => store.dispatch(this);
214214
}
215215

216-
/// /// Dispatch this actions [Stream] to [store].
216+
/// Dispatch this actions [Stream] to [store].
217217
extension DispatchToStreamExtension<A> on Stream<A> {
218218
/// Dispatch this actions [Stream] to [store].
219219
/// See [RxReduxStore.dispatchMany].

lib/src/wrapper_action.dart

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@ import 'package:meta/meta.dart';
44

55
@sealed
66
abstract class ActionType {
7-
const ActionType.empty();
7+
const ActionType._empty();
88

9-
static final initial = _Initial();
10-
static final external = _External();
9+
static const _initial = _Initial();
10+
static const _external = _External();
1111

12-
factory ActionType.sideEffect(int index) = _SideEffect;
12+
static final _sideEffects = <int, _SideEffect>{};
13+
14+
factory ActionType._sideEffect(int index) {
15+
final sideEffect = _sideEffects[index];
16+
if (sideEffect != null) {
17+
return sideEffect;
18+
}
19+
return _sideEffects[index] = _SideEffect(index);
20+
}
1321

1422
@override
1523
String toString() {
@@ -24,22 +32,37 @@ abstract class ActionType {
2432
}
2533

2634
class _Initial extends ActionType {
27-
_Initial() : super.empty();
35+
const _Initial() : super._empty();
2836
}
2937

3038
class _External extends ActionType {
31-
_External() : super.empty();
39+
const _External() : super._empty();
3240
}
3341

3442
class _SideEffect extends ActionType {
3543
final int index;
3644

37-
_SideEffect(this.index) : super.empty();
45+
_SideEffect(this.index) : super._empty();
3846
}
3947

4048
class WrapperAction<A> {
41-
final A? action;
49+
final dynamic _action;
4250
final ActionType type;
4351

44-
WrapperAction(this.action, this.type);
52+
const WrapperAction._(this._action, this.type);
53+
54+
factory WrapperAction.external(A action) =>
55+
WrapperAction._(action, ActionType._external);
56+
57+
factory WrapperAction.sideEffect(A action, int index) =>
58+
WrapperAction._(action, ActionType._sideEffect(index));
59+
60+
static const initial = WrapperAction<Never>._(null, ActionType._initial);
61+
62+
A get action {
63+
if (identical(this, initial)) {
64+
throw StateError('Cannot get action from WrapperAction.initial');
65+
}
66+
return _action as A;
67+
}
4568
}

test/store_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,5 +380,47 @@ void main() {
380380
expect(store.state, 3);
381381
await store.dispose();
382382
});
383+
384+
test('Nullable action', () async {
385+
final store = RxReduxStore<int?, String>(
386+
initialState: '0',
387+
sideEffects: [(actions, getState) => Stream.empty()],
388+
reducer: (s, a) => s + a.toString(),
389+
);
390+
391+
// ignore: unnecessary_cast
392+
(1 as int?).dispatchTo(store);
393+
store.dispatch(2);
394+
await delay(100);
395+
expect(store.state, '012');
396+
397+
store.dispatch(null);
398+
// ignore: unnecessary_cast
399+
(null as int?).dispatchTo(store);
400+
await delay(100);
401+
expect(store.state, '012nullnull');
402+
403+
await store.dispose();
404+
});
405+
406+
test('Nullable state', () async {
407+
final store = RxReduxStore<int, String?>(
408+
initialState: null,
409+
sideEffects: [(actions, getState) => Stream.empty()],
410+
reducer: (s, a) => a == 1 ? '1' : null,
411+
);
412+
413+
expect(store.state, null);
414+
415+
1.dispatchTo(store);
416+
await delay(100);
417+
expect(store.state, '1');
418+
419+
2.dispatchTo(store);
420+
await delay(100);
421+
expect(store.state, null);
422+
423+
await store.dispose();
424+
});
383425
});
384426
}

0 commit comments

Comments
 (0)