ngxs-labs / immer-adapter Goto Github PK
View Code? Open in Web Editor NEW:hamster: Declarative state mutations
License: MIT License
:hamster: Declarative state mutations
License: MIT License
@ImmutableSelector() is not working when using dynamic selector with arguments.
See error below.
First argument to `createDraft` must be a plain object, an array, or an immerable object
repro on stackblitz: https://stackblitz.com/edit/ngxs-repro-hgjaa9?file=src/app/app.state.ts
What is the right way to guarantee immutability when using dynamic selectors?
Could someone release a new version with the updated dependencies for Angular 12?
So in our application we use this immer-adapter to help make our state immutable.
However, we recently ran into an issue where our production build breaks our apps.
It seems that everything is a lot stricter in production builds than in development build, which pointed us to a bunch of errors when using the getState method.
The getState method returns an immer draft, instead of, what we expected, a frozen instance of our state.
As you can see at this line:
https://github.com/ngxs-labs/immer-adapter/blob/master/src/lib/core/immer-adapter/common/immutable-state-context.ts#L23
It just creates a draft, casts it to the provided interface, calls it frozen and then returns it.
We tried to recreate this issue in an isolated project, and the results were the same.
So given an action like this, that finds an object (Dossier) in an array (dossiers) from the state, based on the dossierNumber:
@Action(DossiersActions.DeleteDossierByNumber)
@ImmutableContext()
public deleteDossierByNummer({ getState, setState }: StateContext<DossiersStateModel>, { dossierNumber }: DossiersActions.DeleteDossierByNumber): void {
const found = getState().dossiers.find(d => d.dossierNumber === dossierNumber);
console.log('found', found);
if (!found) {
throw new Error(`Could not find existing dossier to delete, for nummer ${dossierNumber}`);
}
setState((draft: DossiersStateModel) => {
console.log('draft', draft)
delete draft.dossiers[draft.dossiers.indexOf(found)];
draft.dossiers = draft.dossiers.filter(dossier => !!dossier);
return draft;
});
}
We will receive an output like this, and the state would not have been modified (as the found object is nog present in the dossiers list):
found Proxy { ... }
draft Proxy { ... }
However, we would expect something like this (along with the found object to be removed from the dossiers list):
found Dossier { ... }
draft Proxy { ... }
If a working example is required, please do tell and I will try to provide a stack blitz as soon as possible.
The peer dependencies for "@ngxs-labs/immer-adapter": "^3.0.5"
from npm, seems to be having a different peer dependency, than what's in master.
"peerDependencies": {
"@angular/core": "^8.0.2",
"@ngxs/store": "^3.4.0",
"immer": "^3.1.2"
}
What would be required to publish a newer version of this library?
Or, Should we not use this library at all and just use the immer library directly as stated here
When the immer-adapter was using 'produce' as the mutate operator, in some cases the draft was undefined, and I could hadle it with an if. But now it just gives errors, even on Selectors, that I didn't had to handle before.
First I don't know if I should report separately but, when resetting the state using defaults or an object with the type of the State, the state's children are deleted, this is the most common cause of the undefined state. (Sometimes isn't needed to be a children of a reseted state to the state become undefined, but I couldn't reproduce what happens in my application)
The worst for me is the impossibility to handle this when I use immer-adapter.
Here is the stackblitz exemple: https://stackblitz.com/edit/angular-6gaame
Testing this repository I found a new error too, when using a ImmutableContext on a Receiver, other receivers on that state can't mutate the state without ImmutableContext.
Hi there could we release the current master which uses immer 8.0.1 because the current version is affected by prototype pollution.
See here for more details: https://www.npmjs.com/advisories/1603
The following alerts are being generated in my project:
warning " > @ngxs-labs/[email protected]" has incorrect peer dependency "@angular/core@^8.0.2".
warning " > @ngxs-labs/[email protected]" has incorrect peer dependency "immer@^3.1.2"
I noticed that in the project here on github these peer dependencies are updated, but in NPM it is not being updated.
Could you perform this update in NPM please?
I'm trying to reset my state, but I can't. I could use 'this.store.reset(State)', but sometimes I want to wait in somewhere by the action dispatched. And sometimes I want to reset everything except by one attribute. If the state has too many attributes it's easier create a function to assign each attribute to its default.
But the easiest would be just "return defaults", or "return { ...defaults, attribute: draft.attribute } as StateModel"
Here is the stackblitz exemple: https://stackblitz.com/edit/angular-c2nszw
I'm not really sure but as for declaration:
export class FeedZebra {
public static readonly type = '[Animals] Feed zebra';
constructor(public payload: string) {}
}
can it be used as:
@Action(Add)
public add({ getState, setState }: StateContext<AnimalsStateModel>, (...)
?
or should it be:
@Action(FeedZebra)
public add({ getState, setState }: StateContext<AnimalsStateModel>, (...)
?
or even:
@Action(FeedZebra)
public feedZebra({ getState, setState }: StateContext<AnimalsStateModel>, (...)
?
Apparently the use of isDevMode()
in src/lib/core/immer-adapter/decorators/immutable-context.decorator.ts
added in PR #232 breaks production builds, because that function marks the app as initialized, and when enableProdMode()
is executed we get the following error message: Cannot enable prod mode after platform setup
.
This test case (which is a variant of a current test) simulates the error:
import { Component, enableProdMode } from '@angular/core';
(...)
describe('Pizzas state (productionMode = true)', () => {
beforeEach(() => {
enableProdMode();
TestBed.configureTestingModule({
imports: [NgxsModule.forRoot([PizzaState], { developmentMode: false })]
});
store = TestBed.get<Store>(Store);
store.reset({ pizzas: JSON.parse(JSON.stringify(pizzasInitialState)) });
});
it('should add pizza toppings with using immutable mutation', () => {
const previous = store.selectSnapshot(PizzaState);
expect(previous).toEqual(pizzasInitialState);
store.dispatch(new PizzasImmutableAction('tomato ham'));
const newState = store.selectSnapshot(PizzaState);
expect(previous).toEqual(pizzasInitialState);
expect(newState).toEqual({
margherita: {
toppings: ['tomato sauce', 'mozzarella cheese', 'tomato ham'],
prices: { small: '5.00', medium: '6.00', large: '7.00' }
},
prosciutto: {
toppings: ['tomato ham', 'tomato sauce', 'mozzarella cheese', 'ham'],
prices: { small: '6.50', medium: '7.50', large: '8.50' }
}
});
const toppings = store.selectSnapshot(PizzaState.margheritaToppings);
expect(previous).toEqual(pizzasInitialState);
expect(toppings).toEqual(['tomato ham', 'mozzarella cheese', 'tomato sauce']);
});
it('should be correct autobind state context', () => {
const previous = store.selectSnapshot(PizzaState);
expect(previous).toEqual(pizzasInitialState);
store.dispatch(new RemovePriceImmutableAction());
const newState = store.selectSnapshot(PizzaState);
expect(previous).toEqual(pizzasInitialState);
expect(newState).toEqual({
margherita: { toppings: ['tomato sauce', 'mozzarella cheese'], prices: null },
prosciutto: { toppings: ['tomato sauce', 'mozzarella cheese', 'ham'], prices: null }
});
});
});
Here you have an stackblitz example enabling prodMode: https://stackblitz.com/edit/angular-b7niec
For the @ImmutableContext()
you can use setState
, but if you want to use dispatch
as well... how do you combine this?
@Action(SetAutocompleteRequest, { cancelUncompleted: true })
@ImmutableContext()
setAutocompleteRequest({setState, dispatch}: StateContext<ITrialState>, {payload}: SetAutocompleteRequest) {
return this.autocompleteService.postAutocomplete(payload, 5).pipe(
tap(response => {
setState((state: ITrialState) => {
state.autocompleteRequest = {...payload};
return state;
});
// dispatch(
// new SetAutocompleteResponse(response)
// );
})
)
}
The patchState method only offers a shallow patch and as a result is left wanting in more advanced scenarios. This is where state operators come in.
after switching to @ngxs-labs/immer-adapter": "^2.0.1"
, i am getting following error
@Mutation()
@Action(CreateNewConversation)
createConversation(ctx: StateContext<ChatBoxStateModel>) {
const newConversation = new Conversation('payload.conversationId');
ctx.setState((state: ChatBoxStateModel) => {
state.conversations.push(newConversation);
state.activeConversationId = newConversation.id;
return state;
});
}
core.js:5567 ERROR TypeError: 'ownKeys' on proxy: trap result did not include 'scope'
at Function.freeze (<anonymous>)
at deepFreeze (ngxs-store.js:1514)
at ngxs-store.js:1529
at Array.forEach (<anonymous>)
at deepFreeze (ngxs-store.js:1519)
at Object.setState (ngxs-store.js:1607)
at setStateValue (ngxs-store.js:1728)
at Object.setState (ngxs-store.js:1785)
at Object.setState (ngxs-labs-immer-adapter.js:82)
at ChatBoxState.push.../../libs/chat-box/src/lib/state/chat-box.store.ts.ChatBoxState.createConversation (chat-box.store.ts:192)
Hi everybody, i have a doubt. I've been testing the Immer adapter, but the example of the documentation I think is wrong because it gives me typing problems:
so that this error did not give me, I had to return draft, but this does not say it in the documentation, in the documentation this is in the way that gives me error:
So I do not know if I'm the one with the problem
I am having an issue using immer on a Reactive Form in my state. Here is an example of what I am doing.
import { State, Action, StateContext } from '@ngxs/store';
import { ImmutableContext } from '@ngxs-labs/immer-adapter';
export class UpdateName {
public static readonly type = '[Animals] Update Name';
constructor(public name: string) {}
}
@State<AnimalsStateModel>({
name: 'animals',
defaults: {
zebra: new FormGroup({
name: new FormControl('zebra')
}),
}
})
export class AnimalState {
@Action(UpdateName)
@ImmutableContext()
public updateName({ getState, setState }: StateContext<AnimalsStateModel>, payload: UpdateName): void {
setState((state: AnimalsStateModel) => ({
state.zebra.reset(payload);
return state;
}));
}
}
This results in the following error...
Cannot assign to read only property '_pendingValue' of object '[object Object]'
Is there something I am doing wrong?
Versions
@angular/* : "^8.0.0"
@ngxs/store: "^3.6.0"
@ngxs-labs/immer-adapter: "^3.0.5"
Before
@State<AnimalsStateModel>({
name: 'animals',
defaults: {
zebra: {
food: [],
name: 'zebra'
},
panda: {
food: [],
name: 'panda'
}
}
})
export class AnimalState {
@Action(FeedZebra)
public feedZebra(ctx: StateContext<AnimalsStateModel>, { payload }: FeedZebra) {
produce(ctx, (draft: AnimalsStateModel) => draft.zebra.food.push(payload));
}
// OR if you are using ER approach
@Receiver()
public static feedZebra(ctx: StateContext<AnimalsStateModel>, { payload }: EmitterAction<string>) {
produce(ctx, (draft: AnimalsStateModel) => draft.zebra.food.push(payload));
}
}
After
@State<AnimalsStateModel>({
name: 'animals',
defaults: {
zebra: {
food: [],
name: 'zebra'
},
panda: {
food: [],
name: 'panda'
}
}
})
export class AnimalState {
@Mutation()
public static feedZebra(draft, payload: string): AnimalsStateModel {
draft.zebra.food.push(payload);
}
}
Is there a way to obtain the patches and inverse patches (as described in https://immerjs.github.io/immer/docs/patches) using the ImmutableContext() decorator?
Thanks in advance.
when I install @ngxs-labs/immer-adapter
it says it depends on @ngxs-labs/emitter
is it really needed ? I don't see need for @ngxs-labs/emitter
!
npm WARN @ngxs-labs/[email protected] requires a peer of @ngxs-labs/emitter@^1.6.0 but none is installed. You must install peer dependencies yourself.
Before the change from produce operator to ImmutableContext decorator, the Receiver decorator from Emitter plugin had the implementation with immer-adapter documented here, now it doesn't.
It works with the new decorator, but it gives this error, preventing from compiling in aot:
The immer-adapter won't give support anymore to Emmiter plugin?
If you use the ImmutableContext
decorator together with an Action
decorator that is subscribing to an Action that is also subscribed to from another State you get the following error:
ERROR TypeError: Cannot read property 'ctx' of undefined
at push.../../node_modules/@ngxs-labs/immer-adapter/fesm5/ngxs-labs-immer-adapter.js.ImmutableStateContext.getState
// ...
An example to reproduce this:
@State<{}>({
name: 'state1',
defaults: {}
})
export class State1
{
@Action(Action1)
@ImmutableContext()
public onMyAction1({ getState }: StateContext<{}>)
{
getState();
}
}
@State<{}>({
name: 'state2',
defaults: {}
})
export class State2
{
@Action(Action1)
@ImmutableContext()
public onMyAction1({ getState }: StateContext<{}>)
{
getState();
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.