EXPECTED OUTCOME: My goal is to modify a value in my redux state
ISSUE: I am encountering an issue where there is an infinite loop or the browser gets locked down. Despite consulting this Stack Overflow post and the official documentation, I am struggling to identify the mistake in my approach.
This represents my current state:
{ id: 0, product: TV, saleItem: false },
{ id: 1, product: Fridge, saleItem: false }
I intend to update it to
{ id: 0, product: TV, saleItem: true }
{ id: 1, product: Fridge, saleItem: false }
The URL being accessed is: localhost:4200/#/0
I am using a selector to retrieve all items from my store, inspect the URL parameters, and return the corresponding item from the state. The provided URL will return
{ id: 0, product: TV, saleItem: false }
.
I then execute item = { ...item, saleItem: true };
within my effect and trigger the reducer. However, this leads to an endless loop with console.log('before', item);
and console.log('after', item);
repeatedly logging out. Below you can find the code I have implemented along with some alternate attempts I have made.
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.
- Called the selector and passed the value into the action as payload
- Updated the item in the reducer
This led to 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
};
}