基本概念

image.png

我们看下Redux(dart版)源码:

https://github.com/johnpryan/redux.dart/blob/master/doc/basics.md

  1. import 'dart:async';
  2. /// Defines an application's state change
  3. ///
  4. /// Implement this typedef to modify your app state in response to a given
  5. /// action.
  6. ///
  7. /// ### Example
  8. ///
  9. /// int counterReducer(int state, action) {
  10. /// switch (action) {
  11. /// case 'INCREMENT':
  12. /// return state + 1;
  13. /// case 'DECREMENT':
  14. /// return state - 1;
  15. /// default:
  16. /// return state;
  17. /// }
  18. /// }
  19. ///
  20. /// final store = new Store<int>(counterReducer);
  21. typedef Reducer<State> = State Function(State state, dynamic action);
  22. /// Defines a [Reducer] using a class interface.
  23. ///
  24. /// Implement this class to modify your app state in response to a given action.
  25. ///
  26. /// For some use cases, a class may be preferred to a function. In these
  27. /// instances, a ReducerClass can be used.
  28. ///
  29. /// ### Example
  30. ///
  31. /// class CounterReducer extends ReducerClass<int> {
  32. /// int call(int state, action) {
  33. /// switch (action) {
  34. /// case 'INCREMENT':
  35. /// return state + 1;
  36. /// case 'DECREMENT':
  37. /// return state - 1;
  38. /// default:
  39. /// return state;
  40. /// }
  41. /// }
  42. /// }
  43. ///
  44. /// final store = new Store<int>(new CounterReducer());
  45. abstract class ReducerClass<State> {
  46. /// The [Reducer] function that converts the current state and action into a
  47. /// new state
  48. State call(State state, dynamic action);
  49. }
  50. /// A function that intercepts actions and potentially transform actions before
  51. /// they reach the reducer.
  52. ///
  53. /// Middleware intercept actions before they reach the reducer. This gives them
  54. /// the ability to produce side-effects or modify the passed in action before
  55. /// they reach the reducer.
  56. ///
  57. /// ### Example
  58. ///
  59. /// loggingMiddleware(Store<int> store, action, NextDispatcher next) {
  60. /// print('${new DateTime.now()}: $action');
  61. ///
  62. /// next(action);
  63. /// }
  64. ///
  65. /// // Create your store with the loggingMiddleware
  66. /// final store = new Store<int>(
  67. /// counterReducer,
  68. /// middleware: [loggingMiddleware],
  69. /// );
  70. typedef Middleware<State> = dynamic Function(
  71. Store<State> store,
  72. dynamic action,
  73. NextDispatcher next,
  74. );
  75. /// Defines a [Middleware] using a Class interface.
  76. ///
  77. /// Middleware intercept actions before they reach the reducer. This gives them
  78. /// the ability to produce side-effects or modify the passed in action before
  79. /// they reach the reducer.
  80. ///
  81. /// For some use cases, a class may be preferred to a function. In these
  82. /// instances, a MiddlewareClass can be used.
  83. ///
  84. /// ### Example
  85. /// class LoggingMiddleware extends MiddlewareClass<int> {
  86. /// call(Store<int> store, action, NextDispatcher next) {
  87. /// print('${new DateTime.now()}: $action');
  88. ///
  89. /// next(action);
  90. /// }
  91. /// }
  92. ///
  93. /// // Create your store with the loggingMiddleware
  94. /// final store = new Store<int>(
  95. /// counterReducer,
  96. /// middleware: [new LoggingMiddleware()],
  97. /// );
  98. abstract class MiddlewareClass<State> {
  99. /// A [Middleware] function that intercepts a dispatched action
  100. dynamic call(Store<State> store, dynamic action, NextDispatcher next);
  101. }
  102. /// The contract between one piece of middleware and the next in the chain. Use
  103. /// it to send the current action in your [Middleware] to the next piece of
  104. /// [Middleware] in the chain.
  105. ///
  106. /// Middleware can optionally pass the original action or a modified action to
  107. /// the next piece of middleware, or never call the next piece of middleware at
  108. /// all.
  109. typedef NextDispatcher = dynamic Function(dynamic action);
  110. /// Creates a Redux store that holds the app state tree.
  111. ///
  112. /// The only way to change the state tree in the store is to [dispatch] an
  113. /// action. the action will then be intercepted by any provided [Middleware].
  114. /// After running through the middleware, the action will be sent to the given
  115. /// [Reducer] to update the state tree.
  116. ///
  117. /// To access the state tree, call the [state] getter or listen to the
  118. /// [onChange] stream.
  119. ///
  120. /// ### Basic Example
  121. ///
  122. /// // Create a reducer
  123. /// final increment = 'INCREMENT';
  124. /// final decrement = 'DECREMENT';
  125. ///
  126. /// int counterReducer(int state, action) {
  127. /// switch (action) {
  128. /// case increment:
  129. /// return state + 1;
  130. /// case decrement:
  131. /// return state - 1;
  132. /// default:
  133. /// return state;
  134. /// }
  135. /// }
  136. ///
  137. /// // Create the store
  138. /// final store = new Store<int>(counterReducer, initialState: 0);
  139. ///
  140. /// // Print the Store's state.
  141. /// print(store.state); // prints "0"
  142. ///
  143. /// // Dispatch an action. This will be sent to the reducer to update the
  144. /// // state.
  145. /// store.dispatch(increment);
  146. ///
  147. /// // Print the updated state. As an alternative, you can use the
  148. /// // `store.onChange.listen` to respond to all state change events.
  149. /// print(store.state); // prints "1"
  150. class Store<State> {
  151. /// The [Reducer] for your Store. Allows you to get the current reducer or
  152. /// replace it with a new one if need be.
  153. Reducer<State> reducer;
  154. final StreamController<State> _changeController;
  155. State _state;
  156. List<NextDispatcher> _dispatchers;
  157. /// Creates an instance of a Redux Store.
  158. ///
  159. /// The [reducer] argument specifies how the state should be changed in
  160. /// response to dispatched actions.
  161. ///
  162. /// The optional [initialState] argument defines the State of the store when
  163. /// the Store is first created.
  164. ///
  165. /// The optional [middleware] argument takes a list of [Middleware] functions
  166. /// or [MiddlewareClass]. See the [Middleware] documentation for information
  167. /// on how they are used.
  168. ///
  169. /// The [syncStream] argument allows you to use a synchronous
  170. /// [StreamController] instead of an async `StreamController` under the hood.
  171. /// By default, the Stream is async.
  172. Store(
  173. this.reducer, {
  174. State initialState,
  175. List<Middleware<State>> middleware = const [],
  176. bool syncStream = false,
  177. /// If set to true, the Store will not emit onChange events if the new State
  178. /// that is returned from your [reducer] in response to an Action is equal
  179. /// to the previous state.
  180. ///
  181. /// Under the hood, it will use the `==` method from your State class to
  182. /// determine whether or not the two States are equal.
  183. bool distinct = false,
  184. }) : _changeController = StreamController.broadcast(sync: syncStream) {
  185. _state = initialState;
  186. _dispatchers = _createDispatchers(
  187. middleware,
  188. _createReduceAndNotify(distinct),
  189. );
  190. }
  191. /// Returns the current state of the app
  192. State get state => _state;
  193. /// A stream that emits the current state when it changes.
  194. ///
  195. /// ### Example
  196. ///
  197. /// // First, create the Store
  198. /// final store = new Store<int>(counterReducer, 0);
  199. ///
  200. /// // Next, listen to the Store's onChange stream, and print the latest
  201. /// // state to your console whenever the reducer produces a new State.
  202. /// //
  203. /// // We'll store the StreamSubscription as a variable so we can stop
  204. /// // listening later.
  205. /// final subscription = store.onChange.listen(print);
  206. ///
  207. /// // Dispatch some actions, and see the printing magic!
  208. /// store.dispatch("INCREMENT"); // prints 1
  209. /// store.dispatch("INCREMENT"); // prints 2
  210. /// store.dispatch("DECREMENT"); // prints 1
  211. ///
  212. /// // When you want to stop printing the state to the console, simply
  213. /// `cancel` your `subscription`.
  214. /// subscription.cancel();
  215. Stream<State> get onChange => _changeController.stream;
  216. // Creates the base [NextDispatcher].
  217. //
  218. // The base NextDispatcher will be called after all other middleware provided
  219. // by the user have been run. Its job is simple: Run the current state through
  220. // the reducer, save the result, and notify any subscribers.
  221. NextDispatcher _createReduceAndNotify(bool distinct) {
  222. return (dynamic action) {
  223. final state = reducer(_state, action);
  224. if (distinct && state == _state) return;
  225. _state = state;
  226. _changeController.add(state);
  227. };
  228. }
  229. List<NextDispatcher> _createDispatchers(
  230. List<Middleware<State>> middleware,
  231. NextDispatcher reduceAndNotify,
  232. ) {
  233. final dispatchers = <NextDispatcher>[]..add(reduceAndNotify);
  234. // Convert each [Middleware] into a [NextDispatcher]
  235. for (var nextMiddleware in middleware.reversed) {
  236. final next = dispatchers.last;
  237. dispatchers.add(
  238. (dynamic action) => nextMiddleware(this, action, next),
  239. );
  240. }
  241. return dispatchers.reversed.toList();
  242. }
  243. /// Runs the action through all provided [Middleware], then applies an action
  244. /// to the state using the given [Reducer]. Please note: [Middleware] can
  245. /// intercept actions, and can modify actions or stop them from passing
  246. /// through to the reducer.
  247. dynamic dispatch(dynamic action) {
  248. return _dispatchers[0](action);
  249. }
  250. /// Closes down the Store so it will no longer be operational. Only use this
  251. /// if you want to destroy the Store while your app is running. Do not use
  252. /// this method as a way to stop listening to [onChange] state changes. For
  253. /// that purpose, view the [onChange] documentation.
  254. Future teardown() async {
  255. _state = null;
  256. return _changeController.close();
  257. }
  258. }

https://github.com/johnpryan/redux.dart/blob/master/lib/src/utils.dart

  1. import 'package:redux/src/store.dart';
  2. /// A convenience class for binding Reducers to Actions of a given Type. This
  3. /// allows for type safe [Reducer]s and reduces boilerplate.
  4. ///
  5. /// ### Example
  6. ///
  7. /// In order to see what this utility function does, let's take a look at a
  8. /// regular example of using reducers based on the Type of an action.
  9. ///
  10. ///

/// // We define out State and Action classes. /// class AppState { /// final List items; /// /// AppState(this.items); /// } /// /// class LoadItemsAction {} /// class UpdateItemsAction {} /// class AddItemAction{} /// class RemoveItemAction {} /// class ShuffleItemsAction {} /// class ReverseItemsAction {} /// class ItemsLoadedAction { /// final List items; /// /// ItemsLoadedAction(this.items); /// } /// /// // Then we define our reducer. Since we handle different actions in our /// // reducer, we need to determine what kind of action we’re working with /// // using if statements, and then run some computation in response. /// // /// // This isn’t a big deal if we have relatively few cases to handle, but your /// // reducer function can quickly grow large and take on too many /// // responsibilities as demonstrated here with pseudo-code. /// final appReducer = (AppState state, action) { /// if (action is ItemsLoadedAction) { /// return new AppState(action.items); /// } else if (action is UpdateItemsAction) { /// return …; /// } else if (action is AddItemAction) { /// return …; /// } else if (action is RemoveItemAction) { /// return …; /// } else if (action is ShuffleItemsAction) { /// return …; /// } else if (action is ReverseItemsAction) { /// return …; /// } else { /// return state; /// } /// }; /// /// /// What would be nice would be to break our big reducer up into smaller /// reducers. It would also be nice to bind specific Types of Actions to /// specific reducers so we can ensure type safety for our reducers while /// avoiding large trees of `if` statements. /// /// /// // First, we’ll break out all of our individual State Changes into /// // individual reducers. These can be easily tested or composed! /// final loadItemsReducer = (AppState state, LoadTodosAction action) => /// return new AppState(action.items); /// /// final updateItemsReducer = (AppState state, UpdateItemsAction action) { /// return …; /// } /// /// final addItemReducer = (AppState state, AddItemAction action) { /// return …; /// } /// /// final removeItemReducer = (AppState state, RemoveItemAction action) { /// return …; /// } /// /// final shuffleItemsReducer = (AppState state, ShuffleItemAction action) { /// return …; /// } /// /// final reverseItemsReducer = (AppState state, ReverseItemAction action) { /// return …; /// } /// /// // We will then wire up specific types of actions to our reducer functions /// // above. This will return a new Reducer which puts everything /// // together!. /// final Reducer appReducer = combineReducers([ /// new TypedReducer(loadItemsReducer), /// new TypedReducer(updateItemsReducer), /// new TypedReducer(addItemReducer), /// new TypedReducer(removeItemReducer), /// new TypedReducer(shuffleItemsReducer), /// new TypedReducer(reverseItemsReducer), /// ]); /// ``` class TypedReducer implements ReducerClass { /// A [Reducer] function that only accepts an action of a specific type final State Function(State state, Action action) reducer;

/// Creates a reducer that will only be executed if the dispatched action /// matches the [Action] type. TypedReducer(this.reducer);

@override State call(State state, dynamic action) { if (action is Action) { return reducer(state, action); }

  1. return state;

} }

/// A convenience type for binding a piece of Middleware to an Action /// of a specific type. Allows for Type Safe Middleware and reduces boilerplate. /// /// ### Example /// /// In order to see what this utility function does, let’s take a look at a /// regular example of running Middleware based on the Type of an action. /// /// /// class AppState { /// final List<Item> items; /// /// AppState(this.items); /// } /// class LoadItemsAction {} /// class UpdateItemsAction {} /// class AddItemAction{} /// class RemoveItemAction {} /// class ShuffleItemsAction {} /// class ReverseItemsAction {} /// class ItemsLoadedAction<Item> { /// final List<Item> items; /// /// ItemsLoadedAction(this.items); /// } /// /// final loadItems = () { /* Function that loads a Future<List<Item>> */} /// final saveItems = (List<Item> items) { /* Function that persists items */} /// /// final middleware = (Store<AppState> store, action, NextDispatcher next) { /// if (action is LoadItemsAction) { /// loadItems() /// .then((items) => store.dispatch(new ItemsLoaded(items)) /// .catchError((_) => store.dispatch(new ItemsNotLoaded()); /// /// next(action); /// } else if (action is UpdateItemsAction || /// action is AddItemAction || /// action is RemoveItemAction || /// action is ShuffleItemsAction || /// action is ReverseItemsAction) { /// next(action); /// /// saveItems(store.state.items); /// } else { /// next(action); /// } /// }; /// /// /// This works fine if you have one or two actions to handle, but you might /// notice it’s getting a bit messy already. Let’s see how this lib helps clean /// it up. /// /// /// // First, let's start by breaking up our functionality into two middleware /// // functions. /// // /// // The loadItemsMiddleware will only handle the `LoadItemsAction`s that /// // are dispatched, so we can annotate the Type of action. /// final loadItemsMiddleware = ( /// Store<AppState> store, /// LoadItemsAction action, /// NextDispatcher next, /// ) { /// loadItems() /// .then((items) => store.dispatch(new ItemsLoaded(items)) /// .catchError((_) => store.dispatch(new ItemsNotLoaded()); /// /// next(action); /// } /// /// // The saveItemsMiddleware handles all actions that change the Items, but /// // does not depend on the payload of the action. Therefore, `action` will /// // remain dynamic. /// final saveItemsMiddleware = ( /// Store<AppState> store, /// dynamic action, /// NextDispatcher next, /// ) { /// next(action); /// /// saveItems(store.state.items); /// } /// /// // We will then wire up specific types of actions to a List of Middleware /// // that handle those actions. /// final List<Middleware<AppState>> middleware = [ /// new TypedMiddleware<AppState, LoadTodosAction>(loadItemsMiddleware), /// new TypedMiddleware<AppState, AddTodoAction>(saveItemsMiddleware), /// new TypedMiddleware<AppState, ClearCompletedAction>(saveItemsMiddleware), /// new TypedMiddleware<AppState, ToggleAllAction>(saveItemsMiddleware), /// new TypedMiddleware<AppState, UpdateTodoAction>(saveItemsMiddleware), /// new TypedMiddleware<AppState, TodosLoadedAction>(saveItemsMiddleware), /// ]; /// class TypedMiddleware implements MiddlewareClass { /// A [Middleware] function that only works on actions of a specific type. final void Function( Store store, Action action, NextDispatcher next, ) middleware;

/// Create a [Middleware] that is only executed when the dispatched action /// matches the [Action] type. TypedMiddleware(this.middleware);

@override dynamic call(Store store, dynamic action, NextDispatcher next) { if (action is Action) { return middleware(store, action, next); } else { return next(action); } } }

/// Defines a utility function that combines several reducers. /// /// In order to prevent having one large, monolithic reducer in your app, it can /// be convenient to break reducers up into smaller parts that handle more /// specific functionality that can be decoupled and easily tested. /// /// ### Example /// /// helloReducer(state, action) { /// return “hello”; /// } /// /// friendReducer(state, action) { /// return state + “ friend”; /// } /// /// final helloFriendReducer = combineReducers( /// helloReducer, /// friendReducer, /// ); Reducer combineReducers(Iterable> reducers) { return (State state, dynamic action) { for (final reducer in reducers) { state = reducer(state, action); } return state; }; }

```

参考学习资料