I'm using NGXS as my state manager. I have a "menuItems" object that populates my "sidenav" component:
export const menuItems: Array<MenuItem> = [
{
icon: 'assets/circle-logo.png',
label: 'Home',
route: '/home',
selected: false
},
{
icon: 'assets/circle-logo.png',
label: 'Customers',
route: '/customers',
selected: false
},
{
icon: 'assets/circle-logo.png',
label: 'Entries',
route: '/entries',
selected: false
}];
when the application load the first time the image on the "icon" property is loaded normally.
The state is updated when I click on a item from my sidenav:
Then an index is send to the state, to change the property selected to "true" and highlight the item, as seen in the image above.
My problem is when I update the "menuItems" state the "logo-image" is reloaded again. I little "blink" is visible on the screen when the image load again.
This is the code of my sidenav state:
import { State, Action, StateContext } from '@ngxs/store';
import { MenuItem } from '@shared/types';
import { menuItems } from '@components/sidenav/menu-items';
export class SelectMenuItem {
static readonly type = 'SelectMenuItem';
constructor(public itemIndex: number) {}
}
@State<Array<MenuItem>>({
name: 'menuItems',
defaults: menuItems
})
export class SideNavState {
@Action(SelectMenuItem)
selectMenuItem(ctx: StateContext<Array<MenuItem>>, action: SelectMenuItem) {
const { getState, setState } = ctx;
const { itemIndex } = action;
const state = getState();
const currentItem = state.findIndex(s => s.selected);
if (currentItem !== itemIndex) {
const newState = state.map((item, index) => {
if (currentItem > -1 && currentItem === index) {
item = { ...item, selected: false };
}
if (itemIndex === index) {
item = { ...item, selected: true };
}
return item;
});
setState(newState);
}
}
}
Am I not updating correctly the state, or there is a workaround for this behavior?
I think the issue here is that your template is reloading the entire list each item you want to select an item - because you are calling setState
, which will cause anything subscribed to your state to receive an update.
I'd suggest modelling your state differently e.g.
export class SelectMenuItem {
static readonly type = 'SelectMenuItem';
constructor(public itemIndex: number) {}
}
// In the state model, capture the selected item/index
// separate from the list of menu items.
interface MenuItemStateModel {
selectedItem: number,
menuItems: MenuItem[]
}
@State<MenuItemStateMode>({
name: 'menuItems',
defaults: {
selectedItem: null,
menuItems: []
}
})
export class MenuItemState {
@Action(SelectMenuItem)
selectItem({patchState}: StateContext<MenuItemStateModel>, action: SelectMenuItem) {
patchState({selectedItem: action.itemIndex});
}
// .. Load the menu items from API (or hardcoded?)
}
Then remove the 'selected' property from your menuItem type. In the template if you then want to show it highlighted you can bind a style according to whether the itemIndex === state.selectedItem
.
This way you don't modify the list when items are selected/unselected.