Ngrx - update a single item in a list of items

I have several following state tree of the application:

AppState { MyPage1 - MyList - MyItem[] MyPage2 - MySubPage - MyList - MyItem[] MyPage3 - MyItem //can be stand-alone as well } 

So, I have a list of items on the page. I load this list by submitting the action to the MyList reducer. As soon as the elements are loaded, they are passed as an input to MyListComponent, which accordingly creates MyItemComponents with the corresponding inputs. MyItemComponent can also be standalone. Therefore, it does not always depend on MyList.

The fact is that each of MyItems has its own actions (for example, they can be loved or edited). And I'm currently stuck where and what actions should I send in this case. How to determine which item has been updated and which list I should update.

What will be the flow of actions when the user updates one of the elements in the list?

And also - would a MyList state change be detected if I hide one of MyItem directly?

A scenario I could think of (if you have all the relevant actions on MyItem) add additional actions to MyList - for example:

 updateItem(payload: { updateType, //eg like, edit, delete data, //data needed to make an update itemId //index in the MyItem array }) 

Then somehow I launch another action from MyList to the specified MyItem from the array, item starts the corresponding action to update its own state (updated via @Effect) If the update was successful, a new action is launched to update the list.

Honestly, all this seems rather verbose and vague. I'm not even sure how this will be implemented in practice. Actually you need an offer from someone with experience.

ps I should mention that all data is retrieved and sent back from / to the REST api.

Edit: If, after the Integrated Introduction to @ ngrx / store approach, the application will have tones of repeating actions (each container - MyList - will have all the actions from MyItem repeated). Would there be a more suitable approach for my case?

+5
source share
1 answer

The way to do this is to have an auxiliary gearbox that processes the auxiliary part of the state tree.

In the case when you need to send actions to one element of the array, you pass all these actions to the second reducer, which processes one element.

Here is an example news item that has comments. I hope this makes it a little easier.

 import { Action } from '@ngrx/store'; import { AppState } from '../reducer'; import { NewsItem } from './news.model'; import * as newsActions from './news.actions'; export interface NewsState { readonly loading: boolean; readonly entities: NewsItem[]; } export interface AppStateWithNews extends AppState { readonly news: NewsState; } const initialState: NewsState = { loading: false, entities: [], }; const newsItemReducer = (newsItem: NewsItem, action: newsActions.NewsActionTypes): NewsItem => { switch (action.type) { case newsActions.Types.UPDATE: case newsActions.Types.REMOVE: case newsActions.Types.COMMENT_CREATE: case newsActions.Types.COMMENT_REMOVE: return Object.assign({}, newsItem, { action: true }); case newsActions.Types.UPDATE_SUCCESS: return Object.assign({}, action.payload, { action: false }); case newsActions.Types.COMMENT_CREATE_SUCCESS: return Object.assign({}, newsItem, { action: false, comments: [action.payload.comment, ...newsItem.comments] }); case newsActions.Types.COMMENT_REMOVE_SUCCESS: return Object.assign({}, newsItem, { action: false, comments: newsItem.comments.filter(i => i.id !== action.payload.commentId) }); default: return newsItem; } }; export const newsReducer = (state: NewsState = initialState, action: newsActions.NewsActionTypes): NewsState => { switch (action.type) { case newsActions.Types.LIST: return Object.assign({}, state, { loading: true }); case newsActions.Types.LIST_SUCCESS: return { loading: false, entities: action.payload }; case newsActions.Types.CREATE_SUCCESS: return Object.assign({ loading: false, entities: [action.payload, ...state.entities] }); case newsActions.Types.REMOVE_SUCCESS: return Object.assign({ loading: false, entities: state.entities.filter(newsItem => newsItem !== action.payload) }); // Delegate to newsItemReducer case newsActions.Types.UPDATE: case newsActions.Types.UPDATE_SUCCESS: case newsActions.Types.COMMENT_CREATE: case newsActions.Types.COMMENT_CREATE_SUCCESS: case newsActions.Types.COMMENT_REMOVE: case newsActions.Types.COMMENT_REMOVE_SUCCESS: return Object.assign({}, state, { entities: state.entities.map(newsItem => { // Only call sub reducer if the incoming actions id matches if (newsItem.id === (action.payload.newsItem || action.payload).id) { return newsItemReducer(newsItem, action); } return newsItem; }) }); default: return state; } }; 

And see what it's called here - news.actions.

 import { Action } from '@ngrx/Store'; import { NewsItem } from './news.model'; import { Response } from '@angular/http'; export const Types = { LIST: '[News] List', LIST_SUCCESS: '[News] List Success', LIST_ERROR: '[News] List ERROR', CREATE: '[News] Create', CREATE_SUCCESS: '[News] Create Success', CREATE_ERROR: '[News] Create Error', UPDATE: '[News] Update', UPDATE_SUCCESS: '[News] Update Success', UPDATE_ERROR: '[News] Update Error', REMOVE: '[News] Remove', REMOVE_SUCCESS: '[News] Remove Success', REMOVE_ERROR: '[News] Remove Error', COMMENT_CREATE: '[News] Comment Create', COMMENT_CREATE_SUCCESS: '[News] Comment Create Success', COMMENT_CREATE_ERROR: '[News] Comment Create Error', COMMENT_REMOVE: '[News] Comment Remove', COMMENT_REMOVE_SUCCESS: '[News] Comment Remove Success', COMMENT_REMOVE_ERROR: '[News] Comment Remove Error', }; /** * List */ export class List implements Action { type = Types.LIST; constructor(public payload?: any) {} } export class ListSuccess implements Action { type = Types.LIST_SUCCESS; constructor(public payload: NewsItem[]) {} } export class ListError implements Action { type = Types.LIST_ERROR; constructor(public payload: Response) {} } /** * Create */ export class Create implements Action { type = Types.CREATE; constructor(public payload: NewsItem) {} } export class CreateSuccess implements Action { type = Types.CREATE_SUCCESS; constructor(public payload: NewsItem) {} } export class CreateError implements Action { type = Types.CREATE_ERROR; constructor(public payload: Response) {} } /** * Update */ export class Update implements Action { type = Types.UPDATE; constructor(public payload: NewsItem) {} } export class UpdateSuccess implements Action { type = Types.UPDATE_SUCCESS; constructor(public payload: NewsItem) {} } export class UpdateError implements Action { type = Types.UPDATE_ERROR; constructor(public payload: Response) {} } /** * Remove */ export class Remove implements Action { type = Types.REMOVE; constructor(public payload: NewsItem) {} } export class RemoveSuccess implements Action { type = Types.REMOVE_SUCCESS; constructor(public payload: NewsItem) {} } export class RemoveError implements Action { type = Types.REMOVE_ERROR; constructor(public payload: Response) {} } /** * Create Comment */ export class CommentCreate implements Action { type = Types.COMMENT_CREATE; payload: { newsItem: NewsItem, comment: string }; constructor(newsItem: NewsItem, comment: string) { this.payload = { newsItem, comment }; } } export class CommentCreateSuccess implements Action { type = Types.COMMENT_CREATE_SUCCESS; payload: { newsItem: NewsItem, comment: string }; constructor(newsItem: NewsItem, comment: string) { this.payload = { newsItem, comment }; } } export class CommentCreateError implements Action { type = Types.COMMENT_CREATE_ERROR; constructor(public payload: Response) {} } /** * Remove Comment */ export class CommentRemove implements Action { type = Types.COMMENT_REMOVE; payload: { newsItem: NewsItem, commentId: string }; constructor(newsItem: NewsItem, commentId: string) { this.payload = { newsItem, commentId }; } } export class CommentRemoveSuccess implements Action { type = Types.COMMENT_REMOVE_SUCCESS; payload: { newsItem: NewsItem, commentId: string }; constructor(newsItem: NewsItem, commentId: string) { this.payload = { newsItem, commentId }; } } export class CommentRemoveError implements Action { type = Types.COMMENT_REMOVE_ERROR; constructor(public payload: Response) {} } export type NewsActionTypes = List | ListSuccess | ListError | Create | CreateSuccess | CreateError | Update | UpdateSuccess | UpdateError | Remove | RemoveSuccess | RemoveError | CommentCreate | CommentCreateSuccess | CommentCreateError | CommentRemove | CommentRemoveSuccess | CommentRemoveError; 
+11
source

Source: https://habr.com/ru/post/1261484/


All Articles