Skip to content

Commit 939aa87

Browse files
authored
Merge pull request #5 from hoc081098/v2.1.0
2.1.0
2 parents 95be691 + 06ab8a2 commit 939aa87

File tree

6 files changed

+102
-103
lines changed

6 files changed

+102
-103
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 2.1.0 - Aug 28, 2020
2+
3+
- State stream returned from `RxReduxStore` will not replay the latest state
4+
(Use `RxReduxStore.state` getter instead).
5+
16
## 2.0.0 - Aug 27, 2020
27

38
- Added `Logger` which allows logging current state, action and new state.

lib/src/redux_store_stream_transformer.dart

Lines changed: 11 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import 'logger.dart';
66
import 'reducer.dart';
77
import 'reducer_exception.dart';
88
import 'side_effect.dart';
9+
import 'utils.dart';
10+
import 'wrapper_action.dart';
911

1012
/// Redux store stream Extension for Stream of actions.
1113
extension ReduxStoreExt<Action> on Stream<Action> {
@@ -94,7 +96,7 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
9496
Stream<S> bind(Stream<A> stream) {
9597
StreamController<S> controller;
9698
List<StreamSubscription<dynamic>> subscriptions;
97-
StreamController<_WrapperAction<A>> actionController;
99+
StreamController<WrapperAction<A>> actionController;
98100

99101
void onListen() {
100102
S state;
@@ -107,13 +109,13 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
107109
return;
108110
}
109111

110-
void onDataActually(_WrapperAction<A> wrapper) {
112+
void onDataActually(WrapperAction<A> wrapper) {
111113
final action = wrapper.action;
112114
final type = wrapper.type;
113115
final currentState = state;
114116

115117
// add initial state
116-
if (type == _ActionType.initial) {
118+
if (identical(type, ActionType.initial)) {
117119
final message = '\n'
118120
' ⟶ Action : $type\n'
119121
' ⟹ Current state: $currentState';
@@ -149,18 +151,18 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
149151
}
150152
}
151153

152-
actionController = StreamController<_WrapperAction<A>>.broadcast();
154+
actionController = StreamController<WrapperAction<A>>.broadcast();
153155

154156
// Call reducer on each action.
155157
final subscriptionActionController =
156158
actionController.stream.listen(onDataActually);
157159

158160
// Add initial action
159-
actionController.add(_WrapperAction(null, _ActionType.initial));
161+
actionController.add(WrapperAction(null, ActionType.initial));
160162

161163
// Listening to upstream actions
162164
final subscriptionUpstream = stream
163-
.map((action) => _WrapperAction(action, _ActionType.external))
165+
.map((action) => WrapperAction(action, ActionType.external))
164166
.listen(
165167
actionController.add,
166168
onError: controller.addError,
@@ -207,7 +209,7 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
207209
}
208210

209211
Iterable<StreamSubscription<dynamic>> _listenSideEffects(
210-
StreamController<_WrapperAction<A>> actionController,
212+
StreamController<WrapperAction<A>> actionController,
211213
GetState<S> getState,
212214
StreamController<S> controller,
213215
) {
@@ -224,8 +226,8 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
224226
}
225227

226228
return actions
227-
.map((action) =>
228-
_WrapperAction(action, _ActionType.sideEffect(index)))
229+
.map(
230+
(action) => WrapperAction(action, ActionType.sideEffect(index)))
229231
.listen(
230232
actionController.add,
231233
onError: controller.addError,
@@ -236,59 +238,3 @@ class ReduxStoreStreamTransformer<A, S> extends StreamTransformerBase<A, S> {
236238
);
237239
}
238240
}
239-
240-
//
241-
// Internal
242-
//
243-
244-
@sealed
245-
abstract class _ActionType {
246-
const _ActionType.empty();
247-
248-
static const initial = _Initial();
249-
static const external = _External();
250-
251-
const factory _ActionType.sideEffect(int index) = _SideEffect;
252-
253-
@override
254-
String toString() {
255-
if (this is _Initial) {
256-
return '⭍';
257-
}
258-
if (this is _External) {
259-
return '↓';
260-
}
261-
if (this is _SideEffect) {
262-
return '⟳${(this as _SideEffect).index}';
263-
}
264-
throw StateError('Unknown $this');
265-
}
266-
}
267-
268-
class _Initial extends _ActionType {
269-
const _Initial() : super.empty();
270-
}
271-
272-
class _External extends _ActionType {
273-
const _External() : super.empty();
274-
}
275-
276-
class _SideEffect extends _ActionType {
277-
final int index;
278-
279-
const _SideEffect(this.index) : super.empty();
280-
}
281-
282-
class _WrapperAction<A> {
283-
final A action;
284-
final _ActionType type;
285-
286-
_WrapperAction(this.action, this.type);
287-
}
288-
289-
extension _MapIndexedIterableExtensison<T> on Iterable<T> {
290-
Iterable<R> mapIndexed<R>(R Function(int, T) mapper) {
291-
var index = 0;
292-
return map((t) => mapper(index++, t));
293-
}
294-
}

lib/src/store.dart

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class RxReduxStore<A, S> {
3131
final void Function(A) _dispatch;
3232

3333
final GetState<S> _getState;
34-
final Stream<S> Function() _stateStream;
34+
final Stream<S> _stateStream;
3535
final Stream<A> _actionStream;
3636

3737
final Future<void> Function() _dispose;
@@ -62,18 +62,22 @@ class RxReduxStore<A, S> {
6262
bool Function(S previous, S next) equals,
6363
RxReduxLogger logger,
6464
}) {
65-
final actionSubject = StreamController<A>.broadcast(sync: true);
65+
final actionController = StreamController<A>(sync: true);
6666
final actionOutputController = StreamController<A>.broadcast(sync: true);
6767

68-
final stateStream = actionSubject.stream.reduxStore<S>(
69-
initialStateSupplier: () => initialState,
70-
sideEffects: [
71-
...sideEffects,
72-
_onEachActionSideEffect(actionOutputController),
73-
],
74-
reducer: reducer,
75-
logger: logger,
76-
);
68+
final stateStream = actionController.stream
69+
.reduxStore<S>(
70+
initialStateSupplier: () => initialState,
71+
sideEffects: [
72+
...sideEffects,
73+
_onEachActionSideEffect(actionOutputController),
74+
],
75+
reducer: reducer,
76+
logger: logger,
77+
)
78+
.distinct(equals)
79+
.skip(1)
80+
.asBroadcastStream(onCancel: (subscription) => subscription.cancel());
7781

7882
var currentState = initialState;
7983
final subscription = stateStream.listen(
@@ -82,18 +86,14 @@ class RxReduxStore<A, S> {
8286
);
8387

8488
return RxReduxStore._(
85-
actionSubject.add,
89+
actionController.add,
8690
() => currentState,
87-
() => () async* {
88-
yield currentState;
89-
yield* stateStream;
90-
}()
91-
.distinct(equals),
91+
stateStream,
9292
actionOutputController.stream,
93-
() => Future.wait([
94-
actionSubject.close(),
95-
subscription.cancel(),
96-
]),
93+
() async {
94+
await actionController.close();
95+
await subscription.cancel();
96+
},
9797
);
9898
}
9999

@@ -115,7 +115,7 @@ class RxReduxStore<A, S> {
115115
/// return LoginWidget(state); // build widget based on state.
116116
/// },
117117
/// );
118-
Stream<S> get stateStream => _stateStream();
118+
Stream<S> get stateStream => _stateStream;
119119

120120
/// Get current state synchronously.
121121
/// This is useful for filling `initialData` when using `StreamBuilder` in Flutter.

lib/src/utils.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// ignore_for_file: public_member_api_docs
2+
3+
extension MapIndexedIterableExtensison<T> on Iterable<T> {
4+
Iterable<R> mapIndexed<R>(R Function(int, T) mapper) {
5+
var index = 0;
6+
return map((t) => mapper(index++, t));
7+
}
8+
}

lib/src/wrapper_action.dart

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import 'package:meta/meta.dart';
2+
3+
// ignore_for_file: public_member_api_docs, missing_return
4+
5+
@sealed
6+
abstract class ActionType {
7+
const ActionType.empty();
8+
9+
static final initial = _Initial();
10+
static final external = _External();
11+
12+
factory ActionType.sideEffect(int index) = _SideEffect;
13+
14+
@override
15+
String toString() {
16+
if (this is _Initial) {
17+
return '⭍';
18+
}
19+
if (this is _External) {
20+
return '↓';
21+
}
22+
if (this is _SideEffect) {
23+
return '⟳${(this as _SideEffect).index}';
24+
}
25+
}
26+
}
27+
28+
class _Initial extends ActionType {
29+
_Initial() : super.empty();
30+
}
31+
32+
class _External extends ActionType {
33+
_External() : super.empty();
34+
}
35+
36+
class _SideEffect extends ActionType {
37+
final int index;
38+
39+
_SideEffect(this.index) : super.empty();
40+
}
41+
42+
class WrapperAction<A> {
43+
final A action;
44+
final ActionType type;
45+
46+
WrapperAction(this.action, this.type);
47+
}

test/store_test.dart

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,15 @@ void main() {
4646
expect(store.state, 0);
4747
});
4848

49-
test('Get state stream that emits initial state', () {
49+
test('Get state stream that never emits', () {
5050
final store = RxReduxStore<int, int>(
5151
initialState: 0,
5252
sideEffects: [],
5353
reducer: (s, a) => s + a,
5454
);
5555

56-
expect(store.stateStream, emits(0));
56+
store.stateStream.listen((event) => expect(true, isFalse));
57+
Future<void>.delayed(const Duration(seconds: 1));
5758
});
5859

5960
test('Get state stream', () async {
@@ -67,7 +68,6 @@ void main() {
6768
store.stateStream,
6869
emitsInOrder(
6970
<String>[
70-
'0',
7171
'0+1',
7272
'0+1+2',
7373
'0+1+2+3',
@@ -82,7 +82,8 @@ void main() {
8282
store.dispatch(3);
8383

8484
await future;
85-
expect(store.stateStream, emits('0+1+2+3'));
85+
store.stateStream.listen((_) => expect(true, isFalse));
86+
await Future<void>.delayed(const Duration(seconds: 1));
8687
});
8788

8889
test('Get state stream with SideEffects', () async {
@@ -123,7 +124,6 @@ void main() {
123124
store.stateStream,
124125
emitsInOrder(
125126
<int>[
126-
0,
127127
1,
128128
3,
129129
6,
@@ -146,7 +146,6 @@ void main() {
146146
store.stateStream,
147147
emitsInOrder(
148148
<int>[
149-
6,
150149
7,
151150
9,
152151
12,
@@ -286,16 +285,10 @@ void main() {
286285

287286
await rxReduxStore.dispose();
288287

289-
rxReduxStore.stateStream.listen(
290-
expectAsync1(
291-
(v) => expect(v, '0'),
292-
count: 1,
293-
),
294-
onDone: expectAsync0(
295-
() {},
296-
count: 1,
297-
),
298-
);
288+
expect(rxReduxStore.state, '0');
289+
expect(() => rxReduxStore.dispatch(0), throwsStateError);
290+
expect(rxReduxStore.actionStream, emitsDone);
291+
expect(rxReduxStore.stateStream, emitsDone);
299292
});
300293

301294
test('Error handler called when reducer throws', () async {

0 commit comments

Comments
 (0)