Skip to content

Commit 65a9234

Browse files
authored
Merge pull request #7 from davidmarne/stateChange
State Change Handler
2 parents 031300b + bef1036 commit 65a9234

File tree

12 files changed

+190
-49
lines changed

12 files changed

+190
-49
lines changed

README.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
### built_redux
55

66
built_redux is a state management library written in dart that enforces immutability.
7+
built_redux stores can be built with middleware and nested reducers
78

89
Inspired by [redux][redux_git]
910

@@ -26,7 +27,7 @@ Built using [built_value][built_value_git]
2627
built_redux: "^0.1.0"
2728
```
2829
29-
2. Create a script to run generators for generating built_values and redux actions.
30+
2. Create a script to run generators for generating built_values and additional built_redux classes.
3031
```
3132
import 'dart:async';
3233

@@ -122,7 +123,7 @@ var store = new Store<Counter, CounterBuilder, CounterActions>(
122123
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
123124
// However it can also be handy to persist the current state in the localStorage.
124125

125-
store.subscribe(() =>
126+
store.subscribe((_) =>
126127
print(store.state);
127128
)
128129

@@ -204,6 +205,29 @@ store.actions.decrement(1);
204205
// 4
205206
```
206207
208+
Nested reducers can be added to your BuiltReducer. In this example NestedCounter
209+
is another BuiltReducer. In order for nested reducers to work you must mix in
210+
{Built reducer name}ReduceChildren just like BaseCounterReduceChildren is below.
211+
This class will be generated for you by the BuiltReduxGenerator.
212+
```
213+
abstract class BaseCounter extends BuiltReducer<BaseCounter, BaseCounterBuilder>
214+
with BaseCounterReduceChildren
215+
implements Built<BaseCounter, BaseCounterBuilder> {
216+
int get count;
217+
218+
NestedCounter get nestedCounter;
219+
220+
get reducer => _baseReducer;
221+
222+
// Built value boilerplate
223+
BaseCounter._();
224+
factory BaseCounter([updates(BaseCounterBuilder b)]) =>
225+
new _$BaseCounter((BaseCounterBuilder b) => b
226+
..count = 1
227+
..nestedCounter = new NestedCounter().toBuilder());
228+
}
229+
```
230+
207231
[built_value_blog]: https://medium.com/dartlang/darts-built-value-for-immutable-object-models-83e2497922d4
208232
209233
[built_value_git]: https://github.com/google/built_value.dart/

example/lib/react_example/module.dart

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
library module;
22

33
import 'package:recompose_dart/recompose_dart.dart';
4+
import 'package:react_built_redux/react_built_redux.dart';
45
import 'package:over_react/over_react.dart';
56
import 'package:built_value/built_value.dart';
67
import 'package:built_collection/built_collection.dart';
@@ -28,14 +29,14 @@ abstract class TodoProps implements Built<TodoProps, TodoPropsBuilder> {
2829
factory TodoProps([updates(TodoPropsBuilder b)]) = _$TodoProps;
2930
}
3031

31-
var todosReduxBuilder = compose<ReduxProps<AppState, AppStateBuilder, AppStateActions>, TodoProps>([
32-
mapReduxStoreToProps<AppState, TodoProps>(
33-
(ReduxProps<AppState, AppStateBuilder, AppStateActions> reduxProps) =>
32+
var todosReduxBuilder = compose<dynamic, TodoProps>([
33+
mapStoreToProps<AppState, AppStateBuilder, AppStateActions, dynamic, TodoProps>(
34+
(Store<AppState, AppStateBuilder, AppStateActions> store, dynamic _) =>
3435
new TodoProps((TodoPropsBuilder b) => b
35-
..actions = reduxProps.store.actions
36-
..currentGroup = reduxProps.store.state.groups.currentGroup?.toBuilder()
37-
..groups.addAll(reduxProps.store.state.groups.groupMap.asMap())
38-
..todos.addAll(reduxProps.store.state.currentGroupTodos.asMap())
36+
..actions = store.actions
37+
..currentGroup = store.state.groups.currentGroup?.toBuilder()
38+
..groups.addAll(store.state.groups.groupMap.asMap())
39+
..todos.addAll(store.state.currentGroupTodos.asMap())
3940
..title = "redux")),
4041
pure,
4142
lifecycle<TodoProps>(

example/lib/reducers/app_state.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import 'todos.dart';
99
import 'todo.dart';
1010
import 'utils.dart';
1111
import '../middleware/creation_middleware.dart';
12-
import 'package:built_value/serializer.dart';
1312

1413
part 'app_state.g.dart';
1514

example/pubspec.lock

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ packages:
6666
path: ".."
6767
relative: true
6868
source: path
69-
version: "0.1.0"
69+
version: "0.1.1"
7070
built_value:
7171
description:
7272
name: built_value
@@ -126,7 +126,7 @@ packages:
126126
name: dart_style
127127
url: "https://pub.dartlang.org"
128128
source: hosted
129-
version: "1.0.3"
129+
version: "1.0.4"
130130
glob:
131131
description:
132132
name: glob
@@ -249,15 +249,22 @@ packages:
249249
version: "0.24.0"
250250
react:
251251
description:
252-
name: react
253-
url: "https://pub.dartlang.org"
254-
source: hosted
252+
ref: context
253+
resolved-ref: a5af894a95356ca6d7baf08aa0772d2628ecf5ca
254+
url: "git://github.com/greglittlefield-wf/react-dart.git"
255+
source: git
255256
version: "3.2.1"
257+
react_built_redux:
258+
description:
259+
path: "../../react_built_redux"
260+
relative: true
261+
source: path
262+
version: "0.0.0"
256263
recompose_dart:
257264
description:
258-
name: recompose_dart
259-
url: "https://pub.dartlang.org"
260-
source: hosted
265+
path: "../../recompose_dart"
266+
relative: true
267+
source: path
261268
version: "0.0.8"
262269
shelf:
263270
description:

example/pubspec.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ name: example
22

33
dependencies:
44
recompose_dart: '^0.0.5'
5-
built_redux: '^0.0.1'
65

76
dev_dependencies:
87
browser: any
@@ -15,6 +14,15 @@ dev_dependencies:
1514
dependency_overrides:
1615
built_redux:
1716
path: ../.
17+
recompose_dart:
18+
path: ../../recompose_dart/
19+
react_built_redux:
20+
path: ../../react_built_redux/
21+
react:
22+
git:
23+
url: git://github.com/greglittlefield-wf/react-dart.git
24+
ref: context
25+
1826

1927
transformers:
2028
- $dart2js

example/web/index.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:html';
66
import 'package:react/react_client.dart' as react_client;
77
import 'package:react/react_dom.dart' as react_dom;
88
import 'package:built_redux/built_redux.dart';
9+
import 'package:react_built_redux/react_built_redux.dart';
910
import 'package:recompose_dart/recompose_dart.dart';
1011

1112
import 'package:example/example.dart';
@@ -19,8 +20,13 @@ Future main() async {
1920
middleware: [creatorMiddeware],
2021
);
2122

23+
ReactElement base = todosReduxBuilder(null);
24+
print(base);
25+
2226
react_dom.render(
23-
todosReduxBuilder(new ReduxProps(reduxStore)),
27+
(Provider()..store = reduxStore)(
28+
todosReduxBuilder(null),
29+
),
2430
querySelector('#container'),
2531
);
2632
}

lib/built_redux.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export 'src/action.dart';
44
export 'src/built_reducer.dart';
55
export 'src/middleware.dart';
66
export 'src/store.dart';
7+
export 'src/store_change.dart';
78
export 'src/typedefs.dart';

lib/src/store.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ import 'action.dart';
77
import 'built_reducer.dart';
88
import 'middleware.dart';
99
import 'typedefs.dart';
10+
import 'store_change.dart';
1011

11-
/// [Store] is the container of your state.
12+
/// [Store] is the container of your state. It listens for actions, invokes reducers,
13+
/// and publishes changes to the state
1214
class Store<State extends BuiltReducer<State, StateBuilder>,
1315
StateBuilder extends Builder<State, StateBuilder>, Actions extends ReduxActions> {
1416
// stream used for dispatching actions
1517
final StreamController<Action<dynamic>> _dispatch = new StreamController.broadcast();
1618

1719
// stream used to dispatch changes to the state
18-
final StreamController<State> _stateController = new StreamController.broadcast();
20+
final StreamController<StoreChange<State, StateBuilder, dynamic>> _stateController =
21+
new StreamController.broadcast();
1922

2023
// the current state
2124
State _state;
@@ -41,8 +44,8 @@ class Store<State extends BuiltReducer<State, StateBuilder>,
4144
if (_state == state) return;
4245

4346
// update the internal state and publish the change
47+
_stateController.add(new StoreChange<State, StateBuilder, dynamic>(state, _state, action));
4448
_state = state;
45-
_stateController.add(_state);
4649
};
4750

4851
// if middleware is give build the chain
@@ -70,7 +73,7 @@ class Store<State extends BuiltReducer<State, StateBuilder>,
7073
}
7174

7275
/// [subscribe] returns a stream that will be dispatched whenever the state changes
73-
Stream<State> get subscribe => _stateController.stream;
76+
Stream<StoreChange<State, StateBuilder, dynamic>> get subscribe => _stateController.stream;
7477

7578
/// [state] returns the current state
7679
State get state => _state;

lib/src/store_change.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'dart:async';
2+
3+
import 'package:built_value/built_value.dart';
4+
5+
import 'action.dart';
6+
import 'built_reducer.dart';
7+
import 'store.dart';
8+
9+
/// [StoreChange] is the payload for the [Store] subscription
10+
class StoreChange<State extends BuiltReducer<State, StateBuilder>,
11+
StateBuilder extends Builder<State, StateBuilder>, P> {
12+
final State next;
13+
final State prev;
14+
final Action<P> action;
15+
16+
StoreChange(State n, State p, Action<P> a)
17+
: next = n,
18+
prev = p,
19+
action = a;
20+
}
21+
22+
/// [StoreChangeHandler] handles a change the store after an action of type Action<T>
23+
typedef StoreChangeHandler<P, State extends BuiltReducer<State, StateBuilder>,
24+
StateBuilder extends Builder<State, StateBuilder>>(
25+
StoreChange<State, StateBuilder, P> storeChange,
26+
);
27+
28+
/// [StoreChangeHandlerBuilder] allows you to listen to the [Store] and perform a handler for a given
29+
/// set of actions with many different payload types, while maintaining type safety.
30+
/// Each [StoreChangeHandler] added with add<T> must take a [StoreChange] with prev and next of type
31+
/// <State, StateBuilder> an Action of typ Action<T>,
32+
class StoreChangeHandlerBuilder<State extends BuiltReducer<State, StateBuilder>,
33+
StateBuilder extends Builder<State, StateBuilder>, Actions extends ReduxActions> {
34+
final _map = new Map<String, StoreChangeHandler<dynamic, State, StateBuilder>>();
35+
StreamSubscription<StoreChange<State, StateBuilder, dynamic>> _subscription;
36+
37+
void add<Payload>(
38+
ActionName<Payload> aName, StoreChangeHandler<Payload, State, StateBuilder> reducer) {
39+
_map[aName.name] = reducer;
40+
}
41+
42+
build(Store<State, StateBuilder, Actions> store) {
43+
// TODO: dispose this sub
44+
_subscription = store.subscribe.listen((StoreChange<State, StateBuilder, dynamic> storeChange) {
45+
var handler = _map[storeChange.action.name];
46+
if (handler != null) handler(storeChange);
47+
});
48+
}
49+
50+
dispose() {
51+
_subscription.cancel();
52+
}
53+
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: built_redux
2-
version: 0.1.1
2+
version: 0.1.2
33
description:
44
A state management library written in dart that enforces immutability
55
authors:

0 commit comments

Comments
 (0)