Lets talk redux for a brief moment. As you may know there are three main ingredients in redux:
- action,
- reducer,
- store.
From the first look this kinda makes sense. You have your store with is actually your app current state. Reducers are pure functions, as such they are disconnected form everything, just take current state, and action to produce new state. Finally, actions, those are like messengers, caring around data for a reducer (transformer).
Since all actions are (or at least should be) easily serializable, you get this amazing feature of time traveling within your app.
On a down side. It makes it bit harder to reason about the code. Since actions and reducers may be defined in separate files. Also, sometimes the same action may be used with different reducer in potentially many different places. A lot things can happen since you have a freedom to mix-and-match actions and reducer!
Can we do better?
I think we can! Some time ago I have found async_redux library and since then cannot live without it! In HabitChallenge app I went from Provider to rx_command to async_redux and I do not plan to move again!
What is all the fuss with your own reducer? In async_redux, each action is a subclass of ReduxAction
, that have an abstract reduce()
method. So each action, defines its own parameters and have bundled in reducer. Then you are dispatching it as normal redux action.
This way action and reducer are always together, in one file, even more, in one class!
Inside of ReduxAction
you have access to whole state object. So you can use it for computation (if needed) or to get additional data for your REST endpoints.
reduce()
method of course returns whole new state, so you can modify different parts of your app/state at one go, in a single action.
ReduxAction
have useful before()
and after()
methods. In case you need to do something before and/or after the action. Like show and hide progress indicator somewhere in the UI.
If you need to process errors there is wrapError()
method. And global error listener!
Whats more, each of before()
, reduce()
and after()
can be asynchronous! Can also dispatch other actions using dispatch()
call!
Instead of middleware, there is an ActionObserver
interface so that you can do some fancy side effects! If you want to.
Also StoreConnector
widget forces you to use View pattern for all of your widgets! This is also the place where your UI and state meets! Where you extract data from state to show to the user and where you initially dispatch actions.
If you care about your app performance, you should definitely give a try for async_redux. Since BaseModel
class (one that is use to bind your UI to the store) lets you define list of properties that when changed will re-render the widget. Otherwise widget will stay as it is. This is so awesome, because you can use values that were computed base on current state, no need to include all the parts from your store!
Have I mentioned about test-ability? There is a dedicated StoreTester
class, that lets you easily define initial state, then execute an action(s) and verify resulting state. It also lets you check all the actions that were fired (if any) and also verity order and parameters of actions. Finally you can of course verify the state it self.
The key takeaway from all of this, is that you should at least scan through async_redux README.md file and give it a try in a free moment.