In the last post of this series, I’ll demonstrate writing UI code as a set of interactions and share how this facilitates integrating Redux and Flow.
When I look at UI code, I want two things to be as obvious as possible:
A
, B
& C
).A
has happened, what changes did it make to the application state?The clarity of these points is the key to the long and happy development of any UI application.
When my interactions were split between the actions.js
and reducer.js
modules, in order to read or write code, I was having to constantly switch back and forth between the two. Things were even worse if multiple reducers were involved. I realized that I should reorganize the code around the interactions, because either implementing a new or working on an existing one, I’m always working in the context of interaction, not action creators or reducers.
Based on this, I reorganized my folders into UI units, like this one:
The main idea here is to represent interactions as modules.
The simplest possible case is when a synchronous action is dispatched and only one reducer should respond. For example, the interaction module below defines a behavior of modal dialog:
The reducer module no longer contains any logic, it’s just an index of interactions:
createReducer
helper from Redux’s recipes. It makes it possible to have an exact mapping of a dispatched action from an action creator to the action handler in the reducer. It’ll be required for accurate flow typings. Let’s say you requested to PATCH
an entity and the server responded with 200 OK
. At this point to respond on the single dispatch, you must apply 2 changes to the app state:
redux-tree
. The main wins here:
Check out examples on GitHub with live demos: redux-interactions
One additional benefit here is the ability to accurately type Redux parts with Flow. Thanks to Atom, I can view Flow errors in the real-time in my editor. And thanks to Nuclide for superior Flow integration.
Here is the example app I’ll refer to during the rest of this post: redux-interactions/flow
Records
as state containers. You can check out its live version. The whole State
type consists of the many small parts:
Each part is defined in its context, thus all details are encapsulated. Each store is defined as an Immutable Record
. In the end, all of the store types are combined in global State type.
When State
is defined, we can type other Redux parts.
Instead of defining an Action
via a union type as suggested in official example:
It’s defined as just $Subtype
of string:
Yes, it’s less accurate here, but it will be very accurate in the interactions, as you will see below.
At this point we can implement typings for all redux parts
Here’s an example of the Flow warnings in action, when I refactor state property name from postId
to id
:
Sadly, Flow can’t infer types of action creators defined in interaction modules. But it’s possible to import types of JS entities, e.g.:
There are some more limitations, see the README for details.
Thanks for reading this, more great stuff coming soon. Cheers!