Advanced Actions in Redux
Tags: redux, explainlikeimfive, statemanagement, tutorial
Published: 2019-08-07 10:57:46

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