EXPECTED OUTCOME: I'm trying to update a value on my redux state
ISSUE: I'm ending up with an infinite loop / browser locking down. I've read this 'SO' post and the docs but struggling to see where I'm going wrong.
This is my state:
{ id: 0, product: TV, saleItem: false },
{ id: 1, product: Fridge, saleItem: false }
and I want to update it to
{ id: 0, product: TV, saleItem: true }
{ id: 1, product: Fridge, saleItem: false }
my url is: localhost:4200/#/0
I'm using a selector to get all the items within my store, check the url params and return the item in the state. The above url will return { id: 0, product: TV, saleItem: false }
I then run item = { ...item, saleItem: true };
within my effect an fire off the reducer. However this is causing an infinite loop somewhere with console.log('before', item);
and console.log('after', item);
being logged out over and over again. Below is the code I have and some alternatives I've tried
Selector
export const getBasketEntities = createSelector(
getBasketState,
fromItems.getBasketEntities
);
export const getSelectedItem = createSelector(
getBasketEntities,
fromRoot.getRouterState,
(entities, router): Item => {
return router.state && entities[router.state.params.id];
}
);
Component
this.store.dispatch(new fromStore.UpdateItem());
Action
export class UpdateItem implements Action {
readonly type = UPDATE_ITEM;
constructor() {}
}
Effects
// update effect
@Effect()
updateItem$ = this.actions$.ofType(itemActions.UPDATE_ITEM).pipe(
switchMap(() => {
return this.store.select(fromSelectors.getSelectedItem).pipe(
map((item: Item) => {
console.log('before', item);
item = { ...item, saleItem: true };
console.log('after', item);
return new itemActions.UpdateItemSuccess(item);
}),
catchError(error => of(new itemActions.UpdateItemFail(error)))
);
})
);
Reducer
case fromItems.UPDATE_ITEM_SUCCESS: {
const item: Item = action.payload;
console.log('reducer', item);
const entities = {
...state.entities,
[item.id]: item
};
return {
...state,
entities
};
}
UPDATE:
- Removed the selector from the effect.
- Calling the selector and passing the value into the action (payload)
- Updating the item in the reducer
This results in the same outcome.
Component
onUpdate() {
this.store
.select(fromStore.getSelectedItem)
.pipe(
map((item: Item) => {
this.store.dispatch(new fromStore.UpdateItem(item));
})
)
.subscribe()
.unsubscribe();
}
Effect
@Effect()
updateItem$ = this.actions$.ofType(itemActions.UPDATE_ITEM).pipe(
map((action: itemActions.UpdateItem) => action.payload),
map((item: Item) => {
return new itemActions.UpdateItemSuccess(item);
}),
catchError(error => of(new itemActions.UpdateItemFail(error)))
);
Action
export class UpdateItem implements Action {
readonly type = UPDATE_ITEM;
constructor(public payload: Item) {}
}
Reducer
case fromItems.UPDATE_ITEM_SUCCESS: {
const item: Item = action.payload;
const entities = {
...state.entities,
[item.id]: { ...item, saleItem: true }
};
return {
...state,
entities
};
}