Search code examples
reactjsreduxreact-reduxredux-thunk

Redux connect chaining async actions


I am in the process of trying to setup redux, react-redux and redux-thunk. Thinks are generally going well but I have a question about how things are supposed to look when chaining multiple async actions together.

Specifically I have a scenario where the actions can be invoked individually or indirectly by another action which can invoke them. My question is how should selectItem be authored if I want to be idiomatic?

action.js

export function fetchByContext(contextId) { 
    return dispatch => {
        _fetchByContext(messages => { 
            dispatch({ type: RECEIVE_MESSAGES, ... });
        });
    };
};

export function subscribeByContext(contextId) {
    return dispatch => {
        _subscribeByContext(messages => { 
            dispatch({ type: RECEIVE_MESSAGES, ... });
        });
    };
};

export function selectItem(contextId) {
    return dispatch => {
        subscribeByContext(contextId)(dispatch);
        fetchByContext(contextId)(dispatch);
    };
};

Solution

  • I believe the key is that (ref):

    Any return value from the inner function will be available as the return value of dispatch itself

    If the inner functions of fetchByContext(), subscribeByContext() return a promise, these could be chained in series or run in parallel from selectItem(). An untested implementation, assuming neither _fetchByContext() nor _subscribeByContext() return a promise would be:

    export function fetchByContext(contextId) { 
        return dispatch => {
            return new Promise((resolve, reject) => {
                _fetchByContext(messages => { 
                    dispatch({ type: RECEIVE_MESSAGES, ... });
                    resolve(messages);
                });
            });
        };
    };
    
    export function subscribeByContext(contextId) {
        return dispatch => {
            return new Promise((resolve, reject) => {
                _subscribeByContext(messages => { 
                    dispatch({ type: RECEIVE_MESSAGES, ... });
                    resolve(messages);
                });
            });
        };
    };
    
    export function selectItem(contextId) {
        return dispatch => {
            // CALL IN SERIES
            return dispatch(subscribeByContext(contextId))
                .then(() => dispatch(fetchByContext(contextId)));
            // CALL IN PARALLEL (alternative to the code above; this is probably not what you want - just keeping for reference)
            return Promise.all(
                dispatch(subscribeByContext(contextId)),
                dispatch(fetchByContext(contextId))
            );
        };
    }
    

    Again please note the code above is untested, just in hope of giving an idea for the general solution.