angular-redux / ng-redux

Angular bindings for Redux
MIT License
1.16k stars 177 forks source link

Actions must be plain objects. Use custom middleware for async actions. #188

Open nikiben opened 6 years ago

nikiben commented 6 years ago

Getting the following error message in console when using the angular-redux library. Also, Redux won't catch or listen for actions after the error occurs. I've searched, including the documentation but nothing points out to fix the error. Am I missing something?

Error

core.js:1427 ERROR Error: Actions must be plain objects. Use custom middleware for async actions.
    at Object.performAction (<anonymous>:3:2312)
    at liftAction (<anonymous>:2:27846)
    at dispatch (<anonymous>:2:31884)
    at eval (createEpicMiddleware.js:67)
    at SafeSubscriber.dispatch [as _next] (applyMiddleware.js:35)
    at SafeSubscriber.__tryOrUnsub (Subscriber.js:240)
    at SafeSubscriber.next (Subscriber.js:187)
    at Subscriber._next (Subscriber.js:128)
    at Subscriber.next (Subscriber.js:92)
    at SwitchMapSubscriber.notifyNext (switchMap.js:127)

Component

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { select } from '@angular-redux/store';
import { ScheduleActions } from '../store/actions'

@Component({
  selector: 'app-page2',
  templateUrl: './page2.component.html',
  styleUrls: ['./page2.component.css']
})
export class Page2Component implements OnInit {

  @select(['schedule', 'scheduleList']) values: any;
  values: any;
  constructor(public actions: ScheduleActions) { }

  ngOnInit() {
    this.actions.loadSchedule();
  }
}

Actions

//schedule-actions.ts
import { Injectable } from '@angular/core';
import { NgRedux } from '@angular-redux/store';
import { Schedule } from '../../model/schedule.model';

@Injectable()
export class ScheduleActions {
    static readonly LOAD_SCHEDULE = 'LOAD_SCHEDULE';
    static readonly LOAD_SCHEDULE_SUCCESS = 'LOAD_SCHEDULE_SUCCESS';

    constructor(private ngRedux: NgRedux<any>){}

    loadSchedule(){
        this.ngRedux.dispatch({
            type: ScheduleActions.LOAD_SCHEDULE
        });
    }
}

Reducer

//schedule-reducer.ts
import { ScheduleActions } from '../actions';

export interface SCHEDULE_STATE {
    scheduleList: any,
    scheduleDetail: any
}

const initialState: SCHEDULE_STATE = {
    scheduleList: [],
    scheduleDetail: {}
}

export const ScheduleReducer = (state: SCHEDULE_STATE = initialState, action): SCHEDULE_STATE => {
    switch(action.type){
        case ScheduleActions.LOAD_SCHEDULE_SUCCESS:
            return {...state, scheduleList: action.payload };
        case ScheduleActions.LOAD_SCHEDULE_DETAIL_SUCCESS:
            return {...state, scheduleList: action.payload };
        case ScheduleActions.CREATE_SCHEDULE_SUCCESS:
            return {...state, scheduleDetail: action.payload };
        default:
            return state;
    }
}

Epics

//schedule-epic.ts
import { Injectable } from '@angular/core';
import { ActionsObservable, ofType } from 'redux-observable';
import { ScheduleService } from '../services';
import { ScheduleActions } from '../actions';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class ScheduleEpic {
    constructor(private service: ScheduleService, 
        private actions: ScheduleActions
    ){}

    loadScheduleEpic = (action$: ActionsObservable<any>) => {
        return action$.ofType(ScheduleActions.LOAD_SCHEDULE)
        .mergeMap(action => {
            return this.service.loadSchedule().map(result => {
                this.actions.loadScheduleSuccess(result)
            })

        })
    }
}

Service

//schedule-service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Schedule } from '../../model/schedule.model';

@Injectable()
export class ScheduleService {

    private API_URL: String = "http://mockserver.io/v2";

    constructor(private http: HttpClient){}

    loadSchedule(){
        return this.http.get(this.API_URL + '/5a6225153100004f2bde7f27').map(res => res)
    }
}

And off course the setup

.......imports......
.......@NgModule.........
export class AppModule { 
    constructor(
        private ngRedux: NgRedux<any>,
        private rootEpic: RootEpic,
        private devTools: DevToolsExtension
    ){
        this.ngRedux.configureStore(
            rootReducer,
            INITIAL_STATE,
            [createEpicMiddleware(rootEpic.createEpics())],
            devTools.isEnabled() ? [devTools.enhancer()] : []
        )
    }
}
AntJanus commented 6 years ago

@nikiben I think you're on the wrong repo. The angular 2+ ngRedux repository is here: https://github.com/angular-redux/store

Pentiado commented 6 years ago

He might be on wrong repo but same problems happens with ng-redux. I'm using ng-redux for angular 1.x. I've been on ^3.4.0-beta.1 for a long time and I updated yesterday to the newest one and I see "Actions must be plain objects" error. Getting rid of Redux Dev Tools Chrome makes it go away. For now, I just went back to 3.x. Here is my createStore code. Maybe I'm doing something obvious wrong but it's not documented.

$ngReduxProvider.createStoreWith(
        reducer,
        [
          ...someCustomMiddleware
          'ngUiRouterMiddleware',
          thunk,
        ],
        window.__REDUX_DEVTOOLS_EXTENSION__
          ? [window.__REDUX_DEVTOOLS_EXTENSION__()]
          : null,
        {
            ...someState
        }
      );
nikiben commented 6 years ago

@Pentiado I think the issue might be Redux Observable. Take a look at the link below. It might help. From the epic/effect function, I was dispatching an action inside an action instead of returning the action object.

https://stackoverflow.com/questions/48388594/angular-redux-epics

https://github.com/redux-observable/redux-observable/issues/413