1+ # built_redux
2+
13[ ![ Pub] ( https://img.shields.io/pub/v/built_redux.svg )] ( https://pub.dartlang.org/packages/built_redux )
24[ ![ codecov.io] ( http://codecov.io/github/davidmarne/built_redux/coverage.svg?branch=master )] ( http://codecov.io/github/davidmarne/built_redux?branch=master )
35
4- ### built_redux
5-
66built_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 .
88
99Inspired by [ redux] [ redux_git ]
1010
1111Built using [ built_value] [ built_value_git ]
1212
13- ### Framework bindings & examples
14-
15- [ react-dart] [ react-dart ]
13+ ## Framework bindings & examples
1614
1715[ flutter] [ flutter ]
1816
19- [ angular2 ] [ angular2 ]
17+ [ react-dart ] [ react-dart ]
2018
19+ [ angular2] [ angular2 ]
2120
22- ### Using it in your project
21+ ## Using it in your project
2322
2423> __ If you are not familiar with Redux or built_value__
2524>
@@ -31,33 +30,36 @@ Built using [built_value][built_value_git]
3130
32311 . Add the ` built_redux ` package as a dependency in your ` pubspec.yaml ` .
3332
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 ```
6163
6264### Writing a built_redux store
6365
@@ -77,19 +79,12 @@ import 'package:built_redux/built_redux.dart';
7779 factory CounterActions() => new _$CounterActions();
7880 }
7981
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> {
8585 /// [count] value of the counter
8686 int get count;
8787
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-
9388 // Built value constructor. The factory is returning the default state
9489 Counter._();
9590 factory BaseCounter() => new _$BaseCounter._(count: 1);
@@ -98,25 +93,26 @@ import 'package:built_redux/built_redux.dart';
9893
9994// These are reducer functions. They have a (state, action, builder) => void signature.
10095// 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.
10297increment(Counter state, Action<int> action, CounterBuilder builder) =>
103- builder.. count = state.count + action.payload;
98+ builder.count = state.count + action.payload;
10499
105100decrement(Counter state, Action<int> action, CounterBuilder builder) =>
106- builder.. count = state.count - action.payload;
101+ builder.count = state.count - action.payload;
107102
108103 // This is a reducer builder. Use of ReducerBuilder is not required, however it
109104 // is strongly recommended as it gives you static type checking to make sure
110105 // 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>()
114109 ..add(CounterActionsNames.increment, increment)
115110 ..add(CounterActionsNames.decrement, decrement)).build();
116111
117112// Create a Redux store holding the state of your app.
118113// Its API contains three getters: stream, state, and actions.
119114var store = new Store<Counter, CounterBuilder, CounterActions>(
115+ reducer,
120116 new Counter(),
121117 new CounterActions(),
122118);
@@ -133,30 +129,19 @@ store.actions.decrement(1);
133129// 2
134130```
135131
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-
144132### Nested Reducers
145133
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.
149136
150137``` 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> {
154140 int get count;
155141
142+ // Also a built_value
156143 NestedCounter get nestedCounter;
157144
158- get reducer => _baseReducer;
159-
160145 // Built value constructor. The factory is returning the default state
161146 BaseCounter._();
162147 factory BaseCounter() => new _$BaseCounter._(
@@ -165,9 +150,38 @@ abstract class BaseCounter extends Object
165150 );
166151}
167152
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+
168182```
169183
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:
171185
172186``` dart
173187abstract class NestedActions extends ReduxActions {
@@ -179,7 +193,9 @@ abstract class NestedActions extends ReduxActions {
179193 factory NestedActions() => new _$NestedActions();
180194}
181195```
196+
182197Then add them to your main action class like so:
198+
183199``` dart
184200abstract class CounterActions extends ReduxActions {
185201 ActionDispatcher<int> increment;
@@ -194,7 +210,9 @@ abstract class CounterActions extends ReduxActions {
194210```
195211
196212Check the usage:
213+
197214``` dart
215+ // only print the nested counter's count
198216store.stream.listen((_) => print(store.state.nestedCounter.count));
199217
200218// The only way to mutate the internal state is to dispatch an action.
@@ -205,6 +223,7 @@ store.actions.increment(2);
205223```
206224
207225### Writing middleware
226+
208227``` dart
209228 // Define specific actions to be handled by this middleware
210229 // A middleware can also listen to and perform side effects on any actions defined elsewhere
@@ -248,6 +267,7 @@ abstract class CounterActions extends ReduxActions {
248267```
249268
250269Check the usage after adding this middleware
270+
251271``` dart
252272// Create a Redux store holding the state of your app.
253273// Its API is subscribe, state, actions.
@@ -269,6 +289,7 @@ store.actions.decrement(1);
269289```
270290
271291A 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+
272293``` dart
273294NextActionHandler loggingMiddleware(MiddlewareApi<Counter, CounterBuilder, CounterActions> api) =>
274295 (ActionHandler next) => (Action action) {
@@ -280,10 +301,12 @@ NextActionHandler loggingMiddleware(MiddlewareApi<Counter, CounterBuilder, Count
280301```
281302
282303### Observing substate
304+
283305Streams can easily be accessed to observe any piece of your state tree by passing a mapper the store's
284306substateStream function. For example, say I only care about BaseCounter.count
285307in the previous example and I do not want to be notified when BaseCounter.nestedCounter changes.
286308I can create a stream that will only emit an event when BaseCounter.count changes, as so:
309+
287310``` dart
288311final countStream = store.substateStream<int>((BaseCounter state) => state.count);
289312
@@ -296,7 +319,6 @@ store.actions.nestedCounter.increment(2);
296319
297320```
298321
299-
300322[ built_value_blog ] : https://medium.com/dartlang/darts-built-value-for-immutable-object-models-83e2497922d4
301323
302324[ built_value_git ] : https://github.com/google/built_value.dart/
0 commit comments