I will cover some fine grained details about actions and reducers in Redux
To place every reader on track, I'll track this from the beginning -
I'll show minimalistic action payload and raise up the complexity, by the way improving readability with action creators, optional payload and payload structure, reducing smells and attaining this golden:
"That'll work for a while"
Alors, se parti!
Minimum Action Payload
A sample action to toggle an IoT-powered window handler that will open\close the frame
function applyWindowToggler(state, action) {
return state.map(window => window.id === action.id
? Object.assign({}, window, {toggle: !window.toggle})
: window
);
}
Here, toggle
property is a negated value of a copy of an original window
object, it overwrites copy's property, and finally, returns new object to reducer.
Good practice is to keep the action payload to a minimum. Substitute payload's properties for reducer's produced properties.
Imagine, there'd been an authorization token, author of requested action, execution confirmation, api, et cetera. It smells like tight coupling if to process all that in frontend. In such a case, you'd likely come up with some sort of middleware api, or enhancer for Store, to fetch necessary objects.
For example, if there're properties to be added during state's reduction, like dateModified
, put it inside reducer
function applyWindowToggler(state, action) {
return state.windows.map(window => window.id === action.id
? Object.assign({}, window, {toggle: !window.toggle}, {dateModified: () => new Date()})
: window
);
}
Action Type
Actions are evaluated by type
. To reduce smell of uncertainty coming from reducers, there's a practice of action type extraction, avoiding running into type typos.
const SET_THERMOSTAT_TEMP = 'SET_THERMOSTAT_TEMP';
const TOGGLE_WINDOW = 'TOGGLE_WINDOW';
const SHUT_WINDOWS = 'SHUT_WINDOWS';
Moreover, extracting action type as a variable, enables definition of reducer and action types in separate modules. Only import action types to use with specific actions and reducers. After all, one action type can be used in multiple reducers. Check out my Advanced Reducers post!
Action Creator
Action creator is an optional convenience, sometimes breeding confusion. Let's make it clear.
Action creator encapsulates an action and optional payload in a reusable pure function
function shutWindows(reason, ...id) {
return {
type: SHUT_WINDOWS,
reasom,
windows: [...id]
}
store.dispatch(shutWindows('DeepWork time!', [0, 1]);
Action creator will return plain action object, and even though it is not mandatory to use them, this practice provides additional decoupling, reusability and testing facility to your codebase.
Optional Payload
Mind that local state management may be utilized for UI trifles, however, this article is about Redux, not frontend (React \ Vue).
Some say "payloads are optional", "... only action type is required". This nicely couples with entities in views, components like dropdown and modal, however, it is controversial
const TOGGLE_LOGIN_MODAL = 'TOGGLE_LOGIN_MODAL';
const openLoginModal = {
type: TOGGLE_LOGIN_MODAL
};
Did you mention verbosity ? As you can see now, it's really up to developer to have control (true\false ; open\close) with an action, or just toggle a switch having removed payload completely.
const openLoginModal = {
type: TOGGLE_LOGIN_MODAL,
isLoginModalOpen: true
}
We can go little further and write up an action creator to add flexibility to controlled action containing payload.
function doToggleLoginModal(open) = {
return {
type: TOGGLE_LOGIN_MODAL,
isLoginModalOpen: open
};
}
In Redux, actions should strive for abstraction, otherwise expect verbosity and unbearable codebase. But if control over UI may be lost - better not to risk.
Payload Structure
Complex payloads, if contain auth_token
that logs relationships between a person or a device who dispatched the SHUT_WINDOWS
action, would look like
{
type: SHUT_WINDOWS,
authorizedBy: { id: 0, name: 'Timofei Shchepkin' },
windows: [0, 1]
}
Properties would add up horizontally, and, eventually convey the one most important property: type
. Smells like verbose, one might say, and would be right. Nesting payloads one level deeper is therefore suggested to make them visible at a first glance
{
type: SHUT_WINDOWS,
payload: {
authorizedBy: { id: 0, name: 'Timofei Shchepkin' },
windows: [0, 1]
}
}
Top comments (0)