DEV Community

loading...

Advanced Actions in Redux

fosteman profile image Timothy Fosteman ・3 min read

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]
    }
}

Discussion

pic
Editor guide