While reading the ngrx documentation, I understood that only reducers are used to modify the store. However, during coding, I noticed that if I modify any field in the object returned by the selector, the change is applied to the store. I assume I recieve original object from selector.
In my case, I noticed that I am modifying store by modifying the object returned by the selector while filling the form where I bind fields of this object.
Why is it possible, why selector doesn't return just a copy of the store? What is the best practice to avaid such problems and why the problem isn't mentioned in the documentation?
I'm adding code example:
State:
export interface IAppState {
offersList: IOffer[],
}
Actions:
export enum OffersActions {
Get = '[Offer] Get',
GetSuccess = '[Offer] GetSuccess'
}
export const getOffers = createAction(
OffersActions .Get,
);
export const getOffersSuccess = createAction(
GetSuccess .GetSuccess,
props<{offersList: IOffer[]}>()
);
Reducers:
export const offersReducer = createReducer(
[],
on(OffersActions.getOffersSuccess, (state, offers) => (offers.offersList))
);
Effects:
getOffers$ = createEffect(() => this.actions$.pipe(
ofType(getOffers),
mergeMap(() => this.offersService.SearchMany().pipe(
map(res => ({
type: OffersActions.GetSuccess,
offersList: res
}))
))
));
Selectors:
const offers = (state: IAppState) => state.offersList;
export const selectOffers = createSelector(
offers ,
(state: IOffers[]) => state
);
export const selectOfferById = (id) => createSelector(
offer,
(state: IOffer[]) => state.find(x => x.id == id)
)
Usage (inside component):
this.offer$ = this.store
.pipe(select(selectOfferById(this.route.snapshot.params.id)));
<offer-form
[offer]="offer$ | async">
</offer-form>
And now if I modify offer inside OfferFormComponent (for example inside input), the change is applied to the store.
Also two more question:
I have added metaReducer storeFreeze from ngrx-store-freeze. Now I see that the object returned from selector can't be modified - changes inside the input don't applied to the object as well as to the store.
- In storeFreeze README there is:
When mutation occurs, an exception will be thrown .
Why I don't have any exception in my console?
- I want to select some part of store and put retured object into the form. I want to apply all changes to store only if the user click submit button. So the only way to do this is to subscribe on selection, copy the result, modify the cloned object and then use reducer? For example like this:
this.offers$.subscribe(x => this.offers= JSON.parse(JSON.stringify(x)));