Kamran
Kamran

Reputation: 829

How to dispatch multiple actions from redux action creators / thunk

In a small React/Redux app that I am writing, I have a thunk that looks as follow:

updateCategory(category){

        return function(dispatch, getState){

            dispatch({ type : types.UPDATE_CATEGORY, category });
            dispatch({ type : locationTypes.UPDATE_CATEGORY_FOR_ALL_LOCATIONS, category });

        }

    } 

complete code:

    //Category Duck
    import v4 from 'node-uuid';
    import {types as locationTypes} from './locations'
    export const types = {
        UPDATE_CATEGORY:"UPDATE_CATEGORY"
    };

    const initialState = [];

    export function reducer(state = initialState, action){

        switch (action.type) {


            case types.UPDATE_CATEGORY:

                return state.map( item => item.id !== action.category.id ? item : {...action.category} );

            default:
                return state;
        }

    };

    export const actions={

        updateCategory(category){

            return function(dispatch, getState){

                dispatch({ type : types.UPDATE_CATEGORY, category });
                dispatch({ type : locationTypes.UPDATE_CATEGORY_FOR_ALL_LOCATIONS, category });

            }

        }
    }

    //Location Duck
    import v4 from 'node-uuid';

    export const types = {

        UPDATE_CATEGORY_FOR_ALL_LOCATIONS:"UPDATE_CATEGORY_FOR_ALL_LOCATIONS"
    };

    const initialState=[];

    export function reducer(state = initialState, action){

        switch (action.type) {

            case types.UPDATE_CATEGORY_FOR_ALL_LOCATIONS:

                return state.map(item => item.category.id !== action.category.id ? item : { ...item, 'category':{name:action.category.name, id:action.category.id} })

            default:
                return state;
        }

    };

    export const actions={

        updateCategoryForAllLocations(category){
            return { type : types.UPDATE_CATEGORY_FOR_ALL_LOCATIONS, category}
        }
    }


    //configStore

    import { createStore, combineReducers, applyMiddleware, compose } from 'redux';

    import { routerReducer, routerMiddleware } from 'react-router-redux';//, push
    import logger from 'redux-logger';

    import * as storage from './localstorage';
    import throttle from 'lodash/throttle';

    import reducers from './duckes/rootReducer'; // Or wherever you keep your reducers

    import thunk from 'redux-thunk';

    const persistedState = storage.loadState();

    const configureStore = (history) => {

        const store = createStore(
            combineReducers({
                ...reducers,
                // router: routerReducer
            }), 
            persistedState, 
            applyMiddleware(thunk)
        );

        store.subscribe(throttle(() => {
            storage.saveState( store.getState() )
        }),1000);

        return store;

    }

    export default configureStore; 

when calling it from a UI click handler in a connected component like this:

updateCategory({id:1, name:'foo') 

I get back Error: Actions must be plain objects. Use custom middleware for async actions.

Can you advice me on how to solve this or maybe explain why it is happening?

the component with the call looks as follows: /ManageCategoryPage

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';

import {actions as categoryActions} from '../../duckes/categories';
import CategoryForm from './CategoryForm';

import {getElementByID} from '../../utils';

class ManageCategoryPage extends Component {

    constructor(props) {

        super(props);

        //Init state
        this.state = { 
            'category' : Object.assign({},this.props.category),
            'errors':{}
         }

        //Bind functions
        this.saveCategory=this.saveCategory.bind(this);
        this.updateCategoryState=this.updateCategoryState.bind(this);
        this.categoryExists=this.categoryExists.bind(this);
    }

    updateCategoryState(event){
      ...
    }

    categoryExists(category){
       ...
    }

    saveCategory(event){
        event.preventDefault();
        const {category}=this.state;
        this.props.actions.updateCategory(category);
        this.props.history.push('/categories');

    }

    //Render
    render(){

        return (

            <CategoryForm 
                category={this.state.category}
                locations={this.props.locations}
                onChange={this.updateCategoryState}
                onSave={this.saveCategory}
                errors={this.state.errors}/>
        )
    }

}

//Prop Types validation
ManageCategoryPage.propTypes={
    category: PropTypes.object.isRequired,
    locations: PropTypes.array.isRequired,
    categories: PropTypes.array.isRequired,
    actions: PropTypes.object.isRequired
};

//Redux connect
const mapStateToProps = ({locations, categories}, ownProps) => {
    let category={id:'', name:''};
    return {
        category : getElementByID(categories, ownProps.match.params.id) || category,
        locations : locations,
        categories : categories
    };

};

const mapDispatchToProps = (dispatch) => {

    return { 
                'actions': bindActionCreators(categoryActions, dispatch)
           };
};

export default connect(mapStateToProps, mapDispatchToProps)(ManageCategoryPage); 

Upvotes: 1

Views: 4607

Answers (1)

Kamran
Kamran

Reputation: 829

I have found the issue and actually it was a mistake of mine, I had two configStore files and I somehow imported an old version of the configStore file that i created previously and it was missing 'thunk' as a param in applyMiddleware() call, so it means that it was applyMiddleware() instead applyMiddleware(thunk).

Upvotes: 1

Related Questions