27

I have the @ngrx/store package in my angular (4.x) app, and am upgrading from v2.2.2 -> v4.0.0. I can see that the migration notes say:

The payload property has been removed from the Action interface.

However, the example they give seems completely counter intuitive (in my view...).

I have a reducer function which looks like this:

export function titleReducer(state = { company: 'MyCo', site: 'London' }, action: Action): ITitle {
    switch (action.type) {
        case 'SET_TITLE':
            return {
                company: action.payload.company,
                site: action.payload.site,
                department: action.payload.department,
                line: action.payload.line
            }
        case 'RESET':
            return {
                company: 'MyCo',
                site: 'London'
            }
        default:
            return state
    }
}

Which as expected now throws typescript error:

[ts] Property 'payload' does not exist on type 'Action'

But I have no idea from the migration guide what this should be changed too. Any ideas?

George Edwards
  • 8,979
  • 20
  • 78
  • 161

5 Answers5

27

You can create your own action type that has a payload defined, check the example app for reference:

class AddBookAction implements Action {
    readonly type = ADD_BOOK;

    constructor(public payload: Book) {}
}

Then use that type in the reducer:

function reducer(state = initialState, action: AddBookAction): State

Action could be dispatched like this:

this.store.dispatch(new AddBookAction(book));

Note also that the example app combines all the action types that a reducer can take into a single union type:

export type Actions =
    | AddBookAction
    | AddBookSuccessAction

export function reducer(state = initialState, action: Actions): State
Sergey Karavaev
  • 1,721
  • 14
  • 16
  • 1
    After comparing elvirdolic solution on https://github.com/ngrx/platform/issues/31 and other answers on SO this is the best approach. Create action classes, create custom action type that implements Action and adds `payload` property and then dispatch action classes. Clean and more strongly typed solution. – codeepic Aug 10 '17 at 13:20
  • One thing to notice in this solution is that you should not declare the type variable as some typescript variable type. For example (readonly type: string = ADD_BOOK;) will not work! (if we suppose that ADD_BOOK constant is defined as a string). – skiabox Apr 17 '18 at 14:31
8

Ok, it's very interesting topic. I missed new realese (4.0) but I updated libraries in my repo and I saw that I have the same problem.

That's right. Payload attribute was deleted from Action in new release. If are you use effects to dispatch action, solution is simple and can you read them in migration note

but if you want dispatch to pass payload, you can create parametrizied Action in this way:

export interface ActionWithPayload<T> extends Action {
  payload: T;
} 

so if you added this interface you can change reducer in this way:

export class SomeObject {
    company: string;
    site: string;
    department: string;
    line: string;
}

export function titleReducer(state = { company: 'MyCo', site: 'London' }, action: ActionWithPayload<SomeObject>): ITitle {
    switch (action.type) {
        case 'SET_TITLE':
            return {
                company: action.payload.company,
                site: action.payload.site,
                department: action.payload.department,
                line: action.payload.line
            }
            ...

It's the best walkaround, what I found. I must understand better the reason for this change, and if I will find better soulution I added them here

Jaroslaw K.
  • 5,224
  • 2
  • 39
  • 65
3

Please made change in app.module.ts

imports: [
    StoreModule.forRoot({ appTutorial: TutorReducer } as ActionReducerMap<any,any>),
]

It can remove error.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Pragya
  • 31
  • 4
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 11 '22 at 03:25
0

I also found this approach to be helpful: to import the original Action interface with the as clause and extend it with a payload property.

In this case no changes to the rest of the code are necessary: https://blog.dmbcllc.com/upgrade-ngrx-4-x/.

Peter Kassenaar
  • 301
  • 3
  • 4
0

I was here with the same error message but with another cause:

on my builder I had something like:

.addCase(setFlight.type, (state, action) => {
  state.flight = action.payload
});

with the action like this:

const setFlight = createAction<Flight>('flight/set');

and then always got the error "payload does not exist on type action", the thing I should have done was to remove type in the first parametere of .addCase() , so the reducer should look like this:

.addCase(setFlight, (state, action) => {
  state.flight = action.payload
});

Now the payload is available in dropdown and error is gone.

Mori
  • 107
  • 9