Search code examples
javascriptreactjsredux-saga

React Redux Saga: Actions must be plain objects. Use custom middleware for async actions


I have spent so much time on this, still can't tell what's going on, I'm working on a react app to send post request to an api, each time i click the add button to dispatch action, I get an error "Action must be plain object"

Here is actions/index.js

import { createAction } from "redux-actions";
import * as actions from "../actionTypes
export const CreateUserRequest = createAction(actions.CREATE_USER_REQUEST);
export const CreateUserSuccess = createAction(actions.CREATE_USER_SUCCESS);
export const CreateUserError = createAction(actions.CREATE_USER_ERROR);

actionTypes/index.js

export const CREATE_USER_REQUEST = "CREATE_USER_REQUEST";
export const CREATE_USER_SUCCESS = "CREATE_USER_SUCCESS";
export const CREATE_USER_ERROR = "CREATE_USER_ERROR";

reducers/createUser.js

    import * as actions from "../actionTypes";

const initialState = {
  isLoading: false,
  isSuccess: false,
  isError: false
};

const createUser = (state = initialState, action) => {
  switch (action.type) {
    case actions.CREATE_USER_REQUEST:
      return {
        ...state,
        isLoading: true,
        isSuccess: false,
        isError: false
      };
    case actions.CREATE_USER_SUCCESS:
      return {
        ...state,
        isLoading: false,
        isSuccess: true,
        isError: false,
        result: action.payload,
      };

    case actions.CREATE_USER_ERROR:
      return {
        ...state,
        isLoading: false,
        isSuccess: false,
        isError: true,
        result: action.payload,
      };

    default:
      return state;
  }
};

export default createUser;

reducers/index.js

import { combineReducers } from "redux";
import CreateUserReducer from './createUser';
const rootReducer = combineReducers({
  CreateUserStatus:CreateUserReducer,
  //logout:logout,
})

export default rootReducer

saga/createUser.js

import { call, put, takeLatest } from "redux-saga/effects";
import { CreateUserSuccess, CreateUserError, CreateUserRequest } from '../actions/index';
import axiosCall from "../../services";
import * as actions from "../actionTypes"

export function* createUserRequest(action) {
  try {
      const response = yield call(
        axiosCall,
        "POST",
        '/api/createUser',
        action.payload,
      );
  
      if (response) {
        yield put(CreateUserSuccess({  response : response.data}));
      } else {
        yield put(CreateUserError({ error: "Invalid  details" }));
      }
    } catch (error) {
      console.log('errrrrrrrrr::',error)
      yield put(CreateUserError({ error: "Invalid  detailssssssss" }));
    }
  yield takeLatest(actions.CREATE__USER_REQUEST, CreateUserRequest);
}

saga/index.js

import { fork, all } from "redux-saga/effects";
import {createUserRequest} from './createUser';
function* rootSaga() {
  {
    yield all([
      fork(createUserRequest),
    ]);
  }
}

export default rootSaga;

store.js

    import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import rootReducer from "../reducers";
import rootSaga from "../saga/index";
import { createLogger } from "redux-logger";
const middleWares = [];

import { composeWithDevTools } from "redux-devtools-extension";

const logger = createLogger({
  predicate: () => process.env.NODE_ENV === 'development',
  collapsed: true
});

const sagaMiddleware = createSagaMiddleware();

middleWares.push(sagaMiddleware);
middleWares.push(logger);
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(...middleWares)));
sagaMiddleware.run(rootSaga);

export default store;

Component createUser.js

import React, { useState} from 'react';
import { useDispatch } from "react-redux"
import {
  Container,
} from '../../components';
import { createUserRequest } from '../../redux/actions';

const createUser = () => {
  const dispatch = useDispatch()
  const [newuser,setNewuser] = useState({
    first_name : 'Jon',
    last_name : 'Doe',
    email : 'jondoe@gmail.com',
    phone_number: '+91234455',
    status:0
  })
  const handleChange = (e) =>{
    setNewuser({...newuser,[e.target.name] : e.target.value});
  }

    //Add new user 
    const add_user = ()=>{
      dispatch(createUserRequest(newuser));
    }
  return (
    <Container>
      <form>
      <input type="text" className="form-control" name="first_name" onChange={handleChange}/>
      <input type="text" className="form-control" name="last_name" onChange={handleChange}/>
      <input type="text" className="form-control" name="email" onChange={handleChange}/>
      <input type="text" className="form-control" name="phone_number" onChange={handleChange}/>
      <input type="button" className="form-control" onClick={()=>{add_user()}}/>
      </form>
    </Container>
  )
}
export default createUsers;

Error message I am getting now

Uncaught TypeError: Object(...) is not a function
at add_user (createusers.js?2170:55)
at onClick (createusers.js?2170:82)
at HTMLUnknownElement.callCallback (react-dom.development.js?61bb:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js?61bb:237)
at invokeGuardedCallback (react-dom.development.js?61bb:292)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js?61bb:306)
at executeDispatch (react-dom.development.js?61bb:389)
at executeDispatchesInOrder (react-dom.development.js?61bb:414)
at executeDispatchesAndRelease (react-dom.development.js?61bb:3278)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js?61bb:3287)

It points out to this line

dispatch(createUserRequest(newuser));

Solution

  • Issue

    It appears you are attempting to dispatch your saga function, createUserRequest.

    import { createUserRequest } from '../../redux/saga/createUser';
    
    ...
    
    //Add new user 
    const add_user = () => {
      dispatch(createUserRequest({ newuser: newuser }));
    }
    

    Solution

    You need to create an action that accepts a "new user" payload. Perhaps you meant to use CreateUserRequest.

    import { createAction } from "redux-actions";
    import * as actions from "../actionTypes;
    
    export const CreateUserRequest = createAction(actions.CREATE_USER_REQUEST); // <-- ***
    export const CreateUserSuccess = createAction(actions.CREATE_USER_SUCCESS);
    export const CreateUserError = createAction(actions.CREATE_USER_ERROR);
    

    So in the createUser component.

    import React, { useState } from 'react';
    import { useDispatch } from "react-redux";
    import { Container } from '../../components';
    import { CreateUserRequest } from '../../redux/actions';
    
    const createUser = () => {
      const dispatch = useDispatch()
      const [newuser, setNewuser] = useState({
        first_name : '',
        last_name : '',
        email : '',
        phone_number: '',
        status:0
      });
    
      ...
    
      //Add new user 
      const add_user = () => {
        dispatch(CreateUserRequest({ newuser })); // <-- dispatch action payload
      }
    
      return (
        ...
      )
    }