1
+ # built_redux
2
+
1
3
[ ![ Pub] ( https://img.shields.io/pub/v/built_redux.svg )] ( https://pub.dartlang.org/packages/built_redux )
2
4
[ ![ codecov.io] ( http://codecov.io/github/davidmarne/built_redux/coverage.svg?branch=master )] ( http://codecov.io/github/davidmarne/built_redux?branch=master )
3
5
4
- ### built_redux
5
-
6
6
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.
7
+ built_redux is not only an implementation of [ redux ] [ redux_git ] , but also a framework for building middleware and reducers in a type safe manner .
8
8
9
9
Inspired by [ redux] [ redux_git ]
10
10
11
11
Built using [ built_value] [ built_value_git ]
12
12
13
- ### Framework bindings & examples
14
-
15
- [ react-dart] [ react-dart ]
13
+ ## Framework bindings & examples
16
14
17
15
[ flutter] [ flutter ]
18
16
19
- [ angular2 ] [ angular2 ]
17
+ [ react-dart ] [ react-dart ]
20
18
19
+ [ angular2] [ angular2 ]
21
20
22
- ### Using it in your project
21
+ ## Using it in your project
23
22
24
23
> __ If you are not familiar with Redux or built_value__
25
24
>
@@ -31,33 +30,36 @@ Built using [built_value][built_value_git]
31
30
32
31
1 . Add the ` built_redux ` package as a dependency in your ` pubspec.yaml ` .
33
32
34
- ``` yaml
35
- dependencies :
36
- built_redux : " ^5.0.0"
37
- ` ` `
38
-
39
- 2. Create a script to run generators for generating built_values and additional built_redux classes.
40
- ` ` ` dart
41
- import 'dart:async';
42
-
43
- import 'package:build_runner/build_runner.dart';
44
- import 'package:built_value_generator/built_value_generator.dart';
45
- import 'package:source_gen/source_gen.dart';
46
- import 'package:built_redux/generator.dart';
47
-
48
- /// Build the generated files in the built_value chat example.
49
- Future main(List<String> args) async {
50
- await build([
51
- new BuildAction(
52
- new PartBuilder([
53
- new BuiltValueGenerator(),
54
- new BuiltReduxGenerator(),
55
- ]),
56
- ' built_redux' ,
57
- inputs : const ['test/unit/test_counter.dart'])
58
- ], deleteFilesByDefault : true);
59
- }
60
- ```
33
+ ``` yaml
34
+ dependencies :
35
+ built_redux : " ^6.0.0"
36
+ ` ` `
37
+
38
+ 2. Create a script to run generators for generating built_values and built_redux action classes.
39
+ ` ` ` dart
40
+ import 'dart:async';
41
+
42
+ import 'package:build_runner/build_runner.dart';
43
+ import 'package:built_value_generator/built_value_generator.dart';
44
+ import 'package:source_gen/source_gen.dart';
45
+ import 'package:built_redux/generator.dart';
46
+
47
+ /// Build the generated files in the built_value chat example.
48
+ Future main(List<String> args) async {
49
+ await build([
50
+ new BuildAction(
51
+ new PartBuilder([
52
+ new BuiltValueGenerator(),
53
+ new BuiltReduxGenerator(),
54
+ ]),
55
+ ' built_redux' ,
56
+ inputs : const ['test/unit/test_counter.dart'])
57
+ ], deleteFilesByDefault : true);
58
+ }
59
+ ```
60
+
61
+ 3 . Run the build script from the command line to generate your built_values and built_redux action classes
62
+ ``` dart tool/build.dart ```
61
63
62
64
### Writing a built_redux store
63
65
@@ -77,19 +79,12 @@ import 'package:built_redux/built_redux.dart';
77
79
factory CounterActions() => new _$CounterActions();
78
80
}
79
81
80
- // This is a BuiltReducer. It is an implementation of the Built and BuiltReducer
81
- // interfaces.
82
- abstract class Counter extends Object
83
- with CounterReducer
84
- implements Built<Counter, CounterBuilder> {
82
+ // This is a built value. It is an immutable model that implements the Built interface.
83
+ // All of the state in your redux store is contained in a single built value model.
84
+ abstract class Counter implements Built<Counter, CounterBuilder> {
85
85
/// [count] value of the counter
86
86
int get count;
87
87
88
- /// reducer returns a map of actions to reducer functions.
89
- /// the generated implementation of reduce will find a reducer function for an action in this map
90
- /// then call the reducer function to rebuild the state.
91
- get reducer => _reducer;
92
-
93
88
// Built value constructor. The factory is returning the default state
94
89
Counter._();
95
90
factory BaseCounter() => new _$BaseCounter._(count: 1);
@@ -98,25 +93,26 @@ import 'package:built_redux/built_redux.dart';
98
93
99
94
// These are reducer functions. They have a (state, action, builder) => void signature.
100
95
// They describes how an action transforms the state into the next state by applying changes to the builder supplied.
101
- // You are required to builder passed, calling state.rebuild will NOT update the state in your redux store.
96
+ // You are required to use the builder passed, calling state.rebuild will NOT update the state in your redux store.
102
97
increment(Counter state, Action<int> action, CounterBuilder builder) =>
103
- builder.. count = state.count + action.payload;
98
+ builder.count = state.count + action.payload;
104
99
105
100
decrement(Counter state, Action<int> action, CounterBuilder builder) =>
106
- builder.. count = state.count - action.payload;
101
+ builder.count = state.count - action.payload;
107
102
108
103
// This is a reducer builder. Use of ReducerBuilder is not required, however it
109
104
// is strongly recommended as it gives you static type checking to make sure
110
105
// the payload for action name provided is the same as the expected payload
111
- // for the action provided to your reducer. Calling .build() returns the map
112
- // of action names to reducer functions .
113
- var _reducer = (new ReducerBuilder<Counter, CounterBuilder>()
106
+ // for the action provided to your reducer. Calling .build() returns a reducer function
107
+ // that can be passed to the store's constructor .
108
+ var reducer = (new ReducerBuilder<Counter, CounterBuilder>()
114
109
..add(CounterActionsNames.increment, increment)
115
110
..add(CounterActionsNames.decrement, decrement)).build();
116
111
117
112
// Create a Redux store holding the state of your app.
118
113
// Its API contains three getters: stream, state, and actions.
119
114
var store = new Store<Counter, CounterBuilder, CounterActions>(
115
+ reducer,
120
116
new Counter(),
121
117
new CounterActions(),
122
118
);
@@ -133,30 +129,19 @@ store.actions.decrement(1);
133
129
// 2
134
130
```
135
131
136
- # ## Generated Reducer Mixin
137
-
138
- A generated implementation of the BuiltReducer interface will be created for every built value that includes
139
- an extends clause that mixes in a class with the naming scheme {ModelName}Reducer.
140
- (For now this must be the first mixin after the extends clause).
141
- Once the generator is run you can check the .g.dart files for a mixin called {ModelName}Reducer.
142
- In the example above CounterReducer is generated.
143
-
144
132
### Nested Reducers
145
133
146
- A reducer can include vaules that also implement BuiltReducer. In this case NestedCounter
147
- also implements BuiltReducer. This means BaseCounter can handle a different set of actions
148
- than NestedCounter.
134
+ Nested reducers can be built to handle rebuilding built values that are
135
+ nested within the state tree. This is nice for organization and scoping actions to a specific piece of your application's state.
149
136
150
137
``` dart
151
- abstract class BaseCounter extends Object
152
- with BaseCounterReducer
153
- implements Built<BaseCounter, BaseCounterBuilder> {
138
+ // the state model
139
+ abstract class BaseCounter implements Built<BaseCounter, BaseCounterBuilder> {
154
140
int get count;
155
141
142
+ // Also a built_value
156
143
NestedCounter get nestedCounter;
157
144
158
- get reducer => _baseReducer;
159
-
160
145
// Built value constructor. The factory is returning the default state
161
146
BaseCounter._();
162
147
factory BaseCounter() => new _$BaseCounter._(
@@ -165,9 +150,38 @@ abstract class BaseCounter extends Object
165
150
);
166
151
}
167
152
153
+ // the nested model
154
+ abstract class NestedCounter implements Built<NestedCounter, NestedCounterBuilder> {
155
+ int get count;
156
+
157
+ // Built value constructor. The factory is returning the default state
158
+ NestedCounter._();
159
+ factory NestedCounter() => new _$NestedCounter._(
160
+ count: 1,
161
+ );
162
+ }
163
+
164
+ // create a nested reducer builder
165
+ final nestedReducer = new NestedReducerBuilder<BaseCounter, BaseCounterBuilder,
166
+ NestedCounter, NestedCounterBuilder>(
167
+ (state) => state.nestedCounter, // maps the app state to the nested state
168
+ (builder) => builder.nestedCounter, // maps the app builder to the nested builder
169
+ )..add(NestedCounterActionsNames.increment, _nestedIncrement);
170
+
171
+ // actions registered only rebuild the nested state
172
+ // notice the state and builder types are of NestedCounter and NestedCounterBuilder
173
+ _nestedIncrement(NestedCounter state, Action<int> action, NestedCounterBuilder builder) =>
174
+ builder.count = state.count + action.payload;
175
+
176
+ // now use ReducerBuilder.combineNested to add it to your main reducer
177
+ var reducer = (new ReducerBuilder<Counter, CounterBuilder>()
178
+ ..add(CounterActionsNames.increment, increment)
179
+ ..add(CounterActionsNames.decrement, decrement)
180
+ ..combineNested(nestedReducer)).build();
181
+
168
182
```
169
183
170
- Nested actions can also be used to help organize actions for given reducers. First define your actions :
184
+ Nested actions can also be used to help organize actions for nested reducers. First define your actions:
171
185
172
186
``` dart
173
187
abstract class NestedActions extends ReduxActions {
@@ -179,7 +193,9 @@ abstract class NestedActions extends ReduxActions {
179
193
factory NestedActions() => new _$NestedActions();
180
194
}
181
195
```
196
+
182
197
Then add them to your main action class like so:
198
+
183
199
``` dart
184
200
abstract class CounterActions extends ReduxActions {
185
201
ActionDispatcher<int> increment;
@@ -194,7 +210,9 @@ abstract class CounterActions extends ReduxActions {
194
210
```
195
211
196
212
Check the usage:
213
+
197
214
``` dart
215
+ // only print the nested counter's count
198
216
store.stream.listen((_) => print(store.state.nestedCounter.count));
199
217
200
218
// The only way to mutate the internal state is to dispatch an action.
@@ -205,6 +223,7 @@ store.actions.increment(2);
205
223
```
206
224
207
225
### Writing middleware
226
+
208
227
``` dart
209
228
// Define specific actions to be handled by this middleware
210
229
// A middleware can also listen to and perform side effects on any actions defined elsewhere
@@ -248,6 +267,7 @@ abstract class CounterActions extends ReduxActions {
248
267
```
249
268
250
269
Check the usage after adding this middleware
270
+
251
271
``` dart
252
272
// Create a Redux store holding the state of your app.
253
273
// Its API is subscribe, state, actions.
@@ -269,6 +289,7 @@ store.actions.decrement(1);
269
289
```
270
290
271
291
A middleware can also be defined without using a MiddlewareBuilder to execute a function for all actions. For example, the following middleware logs every action dispatched as well the the state after the action was handled:
292
+
272
293
``` dart
273
294
NextActionHandler loggingMiddleware(MiddlewareApi<Counter, CounterBuilder, CounterActions> api) =>
274
295
(ActionHandler next) => (Action action) {
@@ -280,10 +301,12 @@ NextActionHandler loggingMiddleware(MiddlewareApi<Counter, CounterBuilder, Count
280
301
```
281
302
282
303
### Observing substate
304
+
283
305
Streams can easily be accessed to observe any piece of your state tree by passing a mapper the store's
284
306
substateStream function. For example, say I only care about BaseCounter.count
285
307
in the previous example and I do not want to be notified when BaseCounter.nestedCounter changes.
286
308
I can create a stream that will only emit an event when BaseCounter.count changes, as so:
309
+
287
310
``` dart
288
311
final countStream = store.substateStream<int>((BaseCounter state) => state.count);
289
312
@@ -296,7 +319,6 @@ store.actions.nestedCounter.increment(2);
296
319
297
320
```
298
321
299
-
300
322
[ built_value_blog ] : https://medium.com/dartlang/darts-built-value-for-immutable-object-models-83e2497922d4
301
323
302
324
[ built_value_git ] : https://github.com/google/built_value.dart/
0 commit comments