Middleware is a great pattern for adding functionality to Redux’s action-handling pipeline. A piece of middleware needs to examine each action to determine whether it should do something with that action. For that to happen, the middleware needs to know something about the structure of the action. Is it checking a known property value? Or is it looking at the type of the action object itself?
The redux-thunk middleware takes the latter approach. It checks whether the action object is a function. If so, it calls that function with well-known parameters (the dispatch function and a function that returns the Redux state). If the action is not a function, redux-thunk ignores it and passes it on to the next middleware in the pipeline.
There are no hard requirements for what an action looks like. Redux just doesn’t care — it passes each action through the middleware pipeline. You can think of your reducers as being the last item in the pipeline. Just like middleware, they inspect each action to determine whether to modify their state.
Both middleware and reducers need to know what to look for in an action. The Redux docs — and every example you can find — use a standard type property on each action. You don’t have to do it that way, but certainly anyone familiar with Redux and the vast majority of Redux-related libraries will expect you to do it that way. So please do it that way!
If I were creating a Redux middleware library, I’d assume that every action had a type property. (Unless, like redux-thunk, my middleware examined only the action’s type and never actually looked at any properties.)
Similarly, you can imagine other standards being useful. Take error handling: If I could count on finding an error object in the same property of every action, I could write middleware that logged errors or dispatched an ERROR_OCCURRED action which a developer could use to notify the user.
Flux standard action: FSA
Several competing standards already exist, of course. Obligatory link to XKCD.
The most common is Flux Standard Action, or FSA. It’s been around long enough that its name is a holdover from a time when the Flux pattern/implementation hadn’t yet become (nearly) synonymous with Redux. (See my earlier post.)
FSA defines four properties that are allowed on an action:
- type: Required. A string or Symbol indicating the action type.
- payload: Optional. Any value or object containing data related to the action.
- error: Optional. A boolean that, when true, indicates that the payload is an Error object.
- meta: Optional. Any value or object containing data that isn’t part of the payload.
Now, I am a developer, therefore I have some quibbles with that structure. Why not just put the Error object in the error property, so we aren’t overloading payload? How do you know what goes in payload vs meta? Whether something is data or metadata is in the eye of the beholder.
But the point of standards isn’t that everyone — or anyone — likes them. (See this discussion in the FSA repo.) It’s that they create a shared expectation and therefore — hopefully, usually, most of the time — interoperability.
FSA achieves that goal because it is far and away the most common, the most expected, the most “standard” standard. For that reason, I recommend using FSA rather than any other “standard” you might find.
You may also want to adopt more comprehensive standards that apply solely within your application.
For example, you may specify properties within meta that all of your actions use in the same way, such as a user id. Or you may require an error code to be added to all of your error objects. Or you may have a naming convention for all of your action type values — such as suffixing async actions with _REQUESTED, _SUCCEEDED, and _FAILED.
Please just be sure that your standard also conforms to FSA. Really.
Check out redux-actions
If you adopt FSA (and you will, right?), then you can also consider some libraries that are designed to work with it. redux-actions is the most popular.
Don’t be confused by the name; redux-actions works with FSA-compliant actions in the context of Redux. It contains helper methods to make defining your action creators a bit easier and to ensure they really are FSA-compliant.
Among other benefits, redux-actions also allows you to get rid of those pesky switch statements in your reducers. Yay!