0

I am implementing ngxs into an existing codebase. I have imported Immer and then ngxs bridge in hopes to handle side effects easier.

I've followed every example that I can find through google, I always get: core.js:6014 ERROR TypeError: Cannot assign to read only property 'savedPrograms' of object '[object Object]'

I've tried using the @ImmutableContext() decorator to accont for this, but I get the exact same error. I also tried using the produce method, but when i give draft.savedPrograms a new value, it throws the error above.

@Action(UserActions.AddProgram)
  @ImmutableContext()
  public addProgram(ctx: StateContext<UserStateModel>, action) {
    ctx.setState(
      produce((draft: UserStateModel) => {
        draft.user.savedPrograms = action.payload;
      })
    );
  }

The only way i can get this to work is if i use JSON parse/stringify to create a copy of the user and then update the user object.

@Action(UserActions.AddProgram)
  public addProgram(ctx: StateContext<UserStateModel>, action) {
    const state = produce(draft => {
      const copy = JSON.parse(JSON.stringify(draft));
      copy.user.savedPrograms.push(action.payload);
      draft = copy;
    });
    ctx.setState(state);
  }

I'm not quite sure why ImmutableContext doesn't work out of the box

grahamama
  • 23
  • 4
  • Honestly I don't think Immer is the way to go. I know a lot of people like it, but I would tell you to try ngxs without it and see if you have any problems, then if you do, solve those problems. – Richard.Davenport Feb 02 '21 at 21:31
  • @Richard.Davenport yea thats what im starting to lean towards – grahamama Feb 02 '21 at 22:29

1 Answers1

0

From immer-adapter documentation:

import { ImmutableContext, ImmutableSelector } from '@ngxs-labs/immer-adapter';
 
@State<AnimalsStateModel>({
  name: 'animals',
  defaults: {
    zebra: {
      food: [],
      name: 'zebra'
    },
    panda: {
      food: [],
      name: 'panda'
    }
  }
})
export class AnimalState {
  @Selector()
  @ImmutableSelector()
  public static zebraFood(state: AnimalsStateModel): string[] {
    return state.zebra.food.reverse();
  }
 
  @Action(Add)
  @ImmutableContext()
  public add({ setState }: StateContext<AnimalsStateModel>, { payload }: Add): void {
    setState((state: AnimalsStateModel) => ({
      state.zebra.food.push(payload);
      return state;
    }));
  }
}

As I understand you need to remove produce from your code:

@Action(UserActions.AddProgram)
  @ImmutableContext()
  public addProgram(ctx: StateContext<UserStateModel>, action) {
    ctx.setState(
      (draft: UserStateModel) => {
        draft.user.savedPrograms = action.payload;
        return draft; 
      }
    );
  }
Aracturat
  • 413
  • 2
  • 14