简体   繁体   中英

NgRx state add/update as array and keep in sync

How do you update state with ngrx if you need to add more than one object?

For instance, in the example below, one user can be associated with more than one task.

Currently, when all tasks are returned, the store is updated twice, and on each update the last task is replaced by a new one. This is expected behaviour, but how would you initialise an array and capture updates to state as an array of objects and ensure that it is kept in sync ?

task.reducer.ts

import * as TaskActions from './task.actions';
import { Action, createReducer, on } from '@ngrx/store';
import { ITask } from '../../models/task';

export interface State {
  task: ITask | null;
  error: any;
}

const initialState: ITask = {
  basketTotal: 0,
  carePlanPrice: 0,
  category: null,
  completionDate: null
};

export const taskReducer = createReducer(
  initialState,
  on(TaskActions.getData, state => ({ ...state })),
  on(TaskActions.dataReceived, (state, payload) => ({ ...state, payload })),
  on(TaskActions.dataNotReceived, state => ({ ...state })),
  on(TaskActions.signOut, state => ({ ...state })),
  on(TaskActions.signOutSuccess, state => ({ ...state, ...initialState })),
);

export function reducer(state: ITask | undefined, action: Action) {
  return taskReducer(state, action);
}

task.effect.ts

  @Effect()
  getData$ = this.actions$.pipe(
    ofType(TaskActions.getData),
    switchMap(() => {
      return this.afs.collection<ITask>('tasks', ref =>
        ref.where('createdBy', '==', localStorage.getItem('uid'))).stateChanges().pipe(
      );
    }),
    mergeMap(actions => actions),
    map(action => {
      if (action.payload) {
        return TaskActions.dataReceived({ payload: TaskService.parseData(action.payload.doc.data()) });
      } else {
        return TaskActions.dataNotReceived();
      }
    })
  );

task.actions.ts

import { createAction, props } from '@ngrx/store';
import { ITask } from '../../models/task';

export const getData = createAction('[Task] Get Data');
export const dataReceived = createAction('[Task] Data Received', props<{ payload: Partial<ITask> }>());
export const dataNotReceived = createAction('[Task] Data Not Received');
export const signOut = createAction('[Task] Sign Out');
export const signOutSuccess = createAction('[Task] Sign Out Success');

Update:

I tried

on(TaskActions.dataReceived, (state, payload) => 
  ({ 
      ...state, 
      tasks: [...state.tasks,  payload.payload ] 
  })),

and this is the result:

在此处输入图片说明

I was expecting an array with both items in place:

task: [
  { ... }, { ... }
]

The spread syntax can also be used for arrays to create a clone.

on(TaskActions.dataReceived, (state, payload) => 
  ({ 
      ...state, 
      tasks: payload.condition ? [...state.tasks,  payload.newTask ] : []
  })),

See the Spread syntax docs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM