1
|
|
|
import { FetchError } from "../../helpers/httpRequests"; |
2
|
|
|
import { |
3
|
|
|
decrement, |
4
|
|
|
deleteProperty, |
5
|
|
|
filterObjectProps, |
6
|
|
|
getId, |
7
|
|
|
hasKey, |
8
|
|
|
mapToObjectTrans, |
9
|
|
|
} from "../../helpers/queries"; |
10
|
|
|
import { ResourceStatus } from "./types"; |
11
|
|
|
|
12
|
|
|
export enum ActionTypes { |
13
|
|
|
IndexStart = "INDEX_START", |
14
|
|
|
IndexFulfill = "INDEX_FULFILL", |
15
|
|
|
IndexReject = "INDEX_REJECT", |
16
|
|
|
|
17
|
|
|
CreateStart = "CREATE_START", |
18
|
|
|
CreateFulfill = "CREATE_FULFILL", |
19
|
|
|
CreateReject = "CREATE_REJECT", |
20
|
|
|
|
21
|
|
|
UpdateStart = "UPDATE_START", |
22
|
|
|
UpdateFulfill = "UPDATE_FULFILL", |
23
|
|
|
UpdateReject = "UPDATE_REJECT", |
24
|
|
|
|
25
|
|
|
DeleteStart = "DELETE_START", |
26
|
|
|
DeleteFulfill = "DELETE_FULFILL", |
27
|
|
|
DeleteReject = "DELETE_REJECT", |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
export type IndexStartAction = { type: ActionTypes.IndexStart }; |
31
|
|
|
export type IndexFulfillAction<T> = { |
32
|
|
|
type: ActionTypes.IndexFulfill; |
33
|
|
|
payload: T[]; |
34
|
|
|
}; |
35
|
|
|
export type IndexRejectAction = { |
36
|
|
|
type: ActionTypes.IndexReject; |
37
|
|
|
payload: Error | FetchError; |
38
|
|
|
}; |
39
|
|
|
|
40
|
|
|
export type CreateStartAction<T> = { |
41
|
|
|
type: ActionTypes.CreateStart; |
42
|
|
|
meta: { item: T }; |
43
|
|
|
}; |
44
|
|
|
export type CreateFulfillAction<T> = { |
45
|
|
|
type: ActionTypes.CreateFulfill; |
46
|
|
|
payload: T; |
47
|
|
|
meta: { item: T }; |
48
|
|
|
}; |
49
|
|
|
export type CreateRejectAction<T> = { |
50
|
|
|
type: ActionTypes.CreateReject; |
51
|
|
|
payload: Error | FetchError; |
52
|
|
|
meta: { item: T }; |
53
|
|
|
}; |
54
|
|
|
|
55
|
|
|
export type UpdateStartAction<T> = { |
56
|
|
|
type: ActionTypes.UpdateStart; |
57
|
|
|
meta: { id: number; item: T }; |
58
|
|
|
}; |
59
|
|
|
export type UpdateFulfillAction<T> = { |
60
|
|
|
type: ActionTypes.UpdateFulfill; |
61
|
|
|
payload: T; |
62
|
|
|
meta: { id: number; item: T }; |
63
|
|
|
}; |
64
|
|
|
export type UpdateRejectAction<T> = { |
65
|
|
|
type: ActionTypes.UpdateReject; |
66
|
|
|
payload: Error | FetchError; |
67
|
|
|
meta: { id: number; item: T }; |
68
|
|
|
}; |
69
|
|
|
|
70
|
|
|
export type DeleteStartAction = { |
71
|
|
|
type: ActionTypes.DeleteStart; |
72
|
|
|
meta: { id: number }; |
73
|
|
|
}; |
74
|
|
|
export type DeleteFulfillAction = { |
75
|
|
|
type: ActionTypes.DeleteFulfill; |
76
|
|
|
meta: { id: number }; |
77
|
|
|
}; |
78
|
|
|
export type DeleteRejectAction = { |
79
|
|
|
type: ActionTypes.DeleteReject; |
80
|
|
|
payload: Error | FetchError; |
81
|
|
|
meta: { id: number }; |
82
|
|
|
}; |
83
|
|
|
export type AsyncAction<T> = |
84
|
|
|
| IndexStartAction |
85
|
|
|
| IndexFulfillAction<T> |
86
|
|
|
| IndexRejectAction |
87
|
|
|
| CreateStartAction<T> |
88
|
|
|
| CreateFulfillAction<T> |
89
|
|
|
| CreateRejectAction<T> |
90
|
|
|
| UpdateStartAction<T> |
91
|
|
|
| UpdateFulfillAction<T> |
92
|
|
|
| UpdateRejectAction<T> |
93
|
|
|
| DeleteStartAction |
94
|
|
|
| DeleteFulfillAction |
95
|
|
|
| DeleteRejectAction; |
96
|
|
|
|
97
|
|
|
export interface ResourceState<T> { |
98
|
|
|
indexMeta: { |
99
|
|
|
status: ResourceStatus; |
100
|
|
|
pendingCount: number; |
101
|
|
|
error: Error | FetchError | undefined; |
102
|
|
|
}; |
103
|
|
|
createMeta: { |
104
|
|
|
status: ResourceStatus; |
105
|
|
|
pendingCount: number; |
106
|
|
|
error: Error | FetchError | undefined; // Only stores the most recent error; |
107
|
|
|
}; |
108
|
|
|
values: { |
109
|
|
|
[id: string]: { |
110
|
|
|
value: T; |
111
|
|
|
error: Error | FetchError | undefined; |
112
|
|
|
status: ResourceStatus; |
113
|
|
|
pendingCount: number; |
114
|
|
|
}; |
115
|
|
|
}; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
export function initializeState<T extends { id: number }>( |
119
|
|
|
items: T[], |
120
|
|
|
): ResourceState<T> { |
121
|
|
|
return { |
122
|
|
|
indexMeta: { |
123
|
|
|
status: "initial", |
124
|
|
|
pendingCount: 0, |
125
|
|
|
error: undefined, |
126
|
|
|
}, |
127
|
|
|
createMeta: { |
128
|
|
|
status: "initial", |
129
|
|
|
pendingCount: 0, |
130
|
|
|
error: undefined, |
131
|
|
|
}, |
132
|
|
|
values: mapToObjectTrans(items, getId, (item) => ({ |
133
|
|
|
value: item, |
134
|
|
|
error: undefined, |
135
|
|
|
status: "initial", |
136
|
|
|
pendingCount: 0, |
137
|
|
|
})), |
138
|
|
|
}; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
type StateValues<T> = ResourceState<T>["values"]; |
142
|
|
|
|
143
|
|
|
function mergeIndexItem<T extends { id: number }>( |
144
|
|
|
values: StateValues<T>, |
145
|
|
|
item: T, |
146
|
|
|
): StateValues<T> { |
147
|
|
|
if (hasKey(values, item.id)) { |
148
|
|
|
// We leave the pending count as is, in case an update or delete is in progress for this item. |
149
|
|
|
// We do overwrite errors, and set status to "fulfilled" if it was "initial" or "rejected" |
150
|
|
|
return { |
151
|
|
|
...values, |
152
|
|
|
[item.id]: { |
153
|
|
|
...values[item.id], |
154
|
|
|
value: item, |
155
|
|
|
status: values[item.id].status === "pending" ? "pending" : "fulfilled", |
156
|
|
|
error: undefined, |
157
|
|
|
}, |
158
|
|
|
}; |
159
|
|
|
} |
160
|
|
|
return { |
161
|
|
|
...values, |
162
|
|
|
[item.id]: { |
163
|
|
|
value: item, |
164
|
|
|
error: undefined, |
165
|
|
|
status: "fulfilled", |
166
|
|
|
pendingCount: 0, |
167
|
|
|
}, |
168
|
|
|
}; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Updates values in response to INDEX FULFILLED action: |
173
|
|
|
* - Updates the value of existing items without modifying item-specific metadata (related to UPDATE and DELETE requests). |
174
|
|
|
* - Creates new items (with "fulfilled" status metadata). |
175
|
|
|
* - Deletes existing state items that are not part of the new payload. |
176
|
|
|
* @param values |
177
|
|
|
* @param payload |
178
|
|
|
*/ |
179
|
|
|
function mergeIndexPayload<T extends { id: number }>( |
180
|
|
|
values: StateValues<T>, |
181
|
|
|
payload: T[], |
182
|
|
|
): StateValues<T> { |
183
|
|
|
// Update or create a values entry for each item in the payload. |
184
|
|
|
const newValues = payload.reduce(mergeIndexItem, values); |
185
|
|
|
// Delete any values entries that don't exist in the new payload. |
186
|
|
|
const payloadIds = payload.map(getId); |
187
|
|
|
return filterObjectProps(newValues, (item) => |
188
|
|
|
payloadIds.includes(item.value.id), |
189
|
|
|
); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Updates values in response to CREATE FULFILLED action. |
194
|
|
|
* - Adds the new item to values, with "fulfilled" status. |
195
|
|
|
* - Note: If newly created item has the same id as an existing item, update that item instead. |
196
|
|
|
* This should never happen during normal interaction with a REST api. |
197
|
|
|
* @param values |
198
|
|
|
* @param payload |
199
|
|
|
*/ |
200
|
|
|
function mergeCreatePayload<T extends { id: number }>( |
201
|
|
|
values: StateValues<T>, |
202
|
|
|
payload: T, |
203
|
|
|
): StateValues<T> { |
204
|
|
|
if (hasKey(values, payload.id)) { |
205
|
|
|
// It doesn't really make sense for the result of a create request to already exist... |
206
|
|
|
// But we have to trust the latest response from the server. Update the existing item. |
207
|
|
|
return { |
208
|
|
|
...values, |
209
|
|
|
[payload.id]: { |
210
|
|
|
value: payload, |
211
|
|
|
status: values[payload.id].pendingCount <= 1 ? "fulfilled" : "pending", |
212
|
|
|
pendingCount: decrement(values[payload.id].pendingCount), |
213
|
|
|
error: undefined, |
214
|
|
|
}, |
215
|
|
|
}; |
216
|
|
|
} |
217
|
|
|
return { |
218
|
|
|
...values, |
219
|
|
|
[payload.id]: { |
220
|
|
|
value: payload, |
221
|
|
|
error: undefined, |
222
|
|
|
status: "fulfilled", |
223
|
|
|
pendingCount: 0, |
224
|
|
|
}, |
225
|
|
|
}; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Updates values in response to UPDATE START action. |
230
|
|
|
* - Updates metadata for updated item. |
231
|
|
|
* - Does nothing if the item does not yet exist. |
232
|
|
|
* @param values |
233
|
|
|
* @param action |
234
|
|
|
*/ |
235
|
|
|
function mergeUpdateStart<T extends { id: number }>( |
236
|
|
|
values: StateValues<T>, |
237
|
|
|
action: UpdateStartAction<T>, |
238
|
|
|
): StateValues<T> { |
239
|
|
|
if (!hasKey(values, action.meta.id)) { |
240
|
|
|
// Do not update values. We don't want to create a new value in case the request fails and it doesn't represent anything on the server. |
241
|
|
|
// NOTE: if we move to optimistic updates, we should add to values here. |
242
|
|
|
return values; |
243
|
|
|
} |
244
|
|
|
return { |
245
|
|
|
...values, |
246
|
|
|
[action.meta.id]: { |
247
|
|
|
// TODO: if we wanted to do an optimistic update, we could save action.payload.item here. |
248
|
|
|
// But we would need some way to reverse it if it failed. |
249
|
|
|
...values[action.meta.id], |
250
|
|
|
status: "pending", |
251
|
|
|
pendingCount: values[action.meta.id].pendingCount + 1, |
252
|
|
|
error: undefined, |
253
|
|
|
}, |
254
|
|
|
}; |
255
|
|
|
} |
256
|
|
|
/** |
257
|
|
|
* Updates values in response to UPDATE FULFILLED action. |
258
|
|
|
* - Updates metadata for updated item and overwrites value with payload. |
259
|
|
|
* @param values |
260
|
|
|
* @param action |
261
|
|
|
*/ |
262
|
|
|
function mergeUpdateFulfill<T extends { id: number }>( |
263
|
|
|
values: StateValues<T>, |
264
|
|
|
action: UpdateFulfillAction<T>, |
265
|
|
|
): StateValues<T> { |
266
|
|
|
if (!hasKey(values, action.meta.id)) { |
267
|
|
|
// Even though it didn't exist in local state yet, if the server says it exists, it exists. |
268
|
|
|
return { |
269
|
|
|
...values, |
270
|
|
|
[action.meta.id]: { |
271
|
|
|
value: action.payload, |
272
|
|
|
status: "fulfilled", |
273
|
|
|
pendingCount: 0, |
274
|
|
|
error: undefined, |
275
|
|
|
}, |
276
|
|
|
}; |
277
|
|
|
} |
278
|
|
|
return { |
279
|
|
|
...values, |
280
|
|
|
[action.meta.id]: { |
281
|
|
|
value: action.payload, |
282
|
|
|
status: |
283
|
|
|
values[action.meta.id].pendingCount <= 1 ? "fulfilled" : "pending", |
284
|
|
|
pendingCount: decrement(values[action.meta.id].pendingCount), |
285
|
|
|
error: undefined, |
286
|
|
|
}, |
287
|
|
|
}; |
288
|
|
|
} |
289
|
|
|
/** |
290
|
|
|
* Updates values in response to UPDATE REJECTED action. |
291
|
|
|
* - DOES NOT throw error if item does exist, unlike other update mergeUpdate functions. |
292
|
|
|
* UPDATE REJECTED action already represents a graceful response to an error. |
293
|
|
|
* There is no relevant metadata to update, and nowhere to store the error, so return state as is. |
294
|
|
|
* - Otherwise updates metdata for item and overwrites error with payload.f |
295
|
|
|
* @param values |
296
|
|
|
* @param action |
297
|
|
|
*/ |
298
|
|
|
function mergeUpdateReject<T extends { id: number }>( |
299
|
|
|
values: StateValues<T>, |
300
|
|
|
action: UpdateRejectAction<T>, |
301
|
|
|
): StateValues<T> { |
302
|
|
|
if (!hasKey(values, action.meta.id)) { |
303
|
|
|
return values; |
304
|
|
|
} |
305
|
|
|
return { |
306
|
|
|
...values, |
307
|
|
|
[action.meta.id]: { |
308
|
|
|
...values[action.meta.id], |
309
|
|
|
status: values[action.meta.id].pendingCount <= 1 ? "rejected" : "pending", |
310
|
|
|
pendingCount: decrement(values[action.meta.id].pendingCount), |
311
|
|
|
error: action.payload, |
312
|
|
|
}, |
313
|
|
|
}; |
314
|
|
|
} |
315
|
|
|
/** |
316
|
|
|
* Updates values in response to DELETE START action. |
317
|
|
|
* Updates metadata for item if it exists. |
318
|
|
|
* |
319
|
|
|
* Does not throw an error if item does not exist, as there are plausible scenarios (eg mupliple queued DELETE requests) that could cause this. |
320
|
|
|
* @param values |
321
|
|
|
* @param action |
322
|
|
|
*/ |
323
|
|
|
function mergeDeleteStart<T extends { id: number }>( |
324
|
|
|
values: StateValues<T>, |
325
|
|
|
action: DeleteStartAction, |
326
|
|
|
): StateValues<T> { |
327
|
|
|
if (!hasKey(values, action.meta.id)) { |
328
|
|
|
return values; |
329
|
|
|
} |
330
|
|
|
return { |
331
|
|
|
...values, |
332
|
|
|
[action.meta.id]: { |
333
|
|
|
...values[action.meta.id], |
334
|
|
|
status: "pending", |
335
|
|
|
pendingCount: values[action.meta.id].pendingCount + 1, |
336
|
|
|
error: undefined, |
337
|
|
|
}, |
338
|
|
|
}; |
339
|
|
|
} |
340
|
|
|
/** |
341
|
|
|
* Updates values in response to DELETE FULFILLED action. |
342
|
|
|
* Deletes the entire value entry, metadata included. (No effect if entry already doesn't exist.) |
343
|
|
|
* |
344
|
|
|
* Note: We can safely delete the metadata because any subsequent DELETE or UPDATE requests |
345
|
|
|
* on the same item will presumably be REJECTED by the REST api. |
346
|
|
|
* DELETE REJECTED and UPDATE REJECTED actions are gracefully handled by the reducer, |
347
|
|
|
* even when no metadata is present. |
348
|
|
|
* @param values |
349
|
|
|
* @param action |
350
|
|
|
*/ |
351
|
|
|
function mergeDeleteFulfill<T extends { id: number }>( |
352
|
|
|
values: StateValues<T>, |
353
|
|
|
action: DeleteFulfillAction, |
354
|
|
|
): StateValues<T> { |
355
|
|
|
return deleteProperty(values, action.meta.id); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Updates values in response to DELETE REJECTED action. |
360
|
|
|
* Updates metadata for item if it exists. |
361
|
|
|
* |
362
|
|
|
* Does not throw an error if item does not exist, as there are plausible scenarios (eg mupliple queued DELETE requests) that could cause this. |
363
|
|
|
* @param values |
364
|
|
|
* @param action |
365
|
|
|
*/ |
366
|
|
|
function mergeDeleteReject<T extends { id: number }>( |
367
|
|
|
values: StateValues<T>, |
368
|
|
|
action: DeleteRejectAction, |
369
|
|
|
): StateValues<T> { |
370
|
|
|
if (!hasKey(values, action.meta.id)) { |
371
|
|
|
return values; |
372
|
|
|
} |
373
|
|
|
return { |
374
|
|
|
...values, |
375
|
|
|
[action.meta.id]: { |
376
|
|
|
...values[action.meta.id], |
377
|
|
|
status: values[action.meta.id].pendingCount <= 1 ? "rejected" : "pending", |
378
|
|
|
pendingCount: decrement(values[action.meta.id].pendingCount), |
379
|
|
|
error: action.payload, |
380
|
|
|
}, |
381
|
|
|
}; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* This Reducer manages the lifecycle of several http requests related to a single type of resource. |
386
|
|
|
* It helps keep a local version of a list of entities in sync with a REST server. |
387
|
|
|
* |
388
|
|
|
* There are 4 types of request: |
389
|
|
|
* - INDEX requests fetch a list of items from the server. |
390
|
|
|
* - CREATE requests create add a new item to the list. |
391
|
|
|
* - UPDATE requests modify a single existing item in the list. |
392
|
|
|
* - DELETE requests remove a single existing item from the list. |
393
|
|
|
* Every request has a lifecycle reflected by 3 possible states, resulting in a total of 12 possible reducer Actions. |
394
|
|
|
* - START: every request begins with a START action. |
395
|
|
|
* - FULFILLED: a successful request dispatches a FULFILLED action, with the response as its payload. |
396
|
|
|
* - REJECTED: a request that fails for any reason dispatches a REJECTED action, with the Error as its payload. |
397
|
|
|
* Any data sent with the requests is included in the actions (in all three states) as metadata. |
398
|
|
|
* |
399
|
|
|
* The Reducer's State contains: |
400
|
|
|
* - values: a map of items and associated request metadata (specifically UPDATE and DELETE request metadata) |
401
|
|
|
* - indexMeta: metadata associated with INDEX requests, as they don't relate to specific items |
402
|
|
|
* - createMeta: metadata associated with CREATE requests, as they don't relate to existing items |
403
|
|
|
* |
404
|
|
|
* The metadata associated with a request includes: |
405
|
|
|
* - status: one of four values: |
406
|
|
|
* - "initial" if a request has never been made |
407
|
|
|
* - "pending" if ANY request is in progress which could modify this resource |
408
|
|
|
* - "fulfilled" if the last completed request succeeded and no other request is in progress |
409
|
|
|
* - "rejected" if the last completed request failed and no other request is in progress |
410
|
|
|
* - pendingCount: stores the number of requests in progress. This helps account for the possibility of multiple requests being started in succession, and means one request could finish and the resource still be considered "pending". |
411
|
|
|
* - error: stores the last error recieved from a REJECTED action. Overwritten with undefined if a later request is STARTed or FULFILLED. |
412
|
|
|
* |
413
|
|
|
* Notes about item values: |
414
|
|
|
* - Its possible to include items in the initial state and then not begin any requests, in which case there will be existing values with the "initial" status. |
415
|
|
|
* - REJECTED actions do not overwrite the value. Therefore when a request fails and status becomes "rejected", the last good value is still available (though it may become out-of-sync with the REST api). |
416
|
|
|
* @param state |
417
|
|
|
* @param action |
418
|
|
|
*/ |
419
|
|
|
export function reducer<T extends { id: number }>( |
420
|
|
|
state: ResourceState<T>, |
421
|
|
|
action: AsyncAction<T>, |
422
|
|
|
): ResourceState<T> { |
423
|
|
|
switch (action.type) { |
424
|
|
|
case ActionTypes.IndexStart: |
425
|
|
|
return { |
426
|
|
|
...state, |
427
|
|
|
indexMeta: { |
428
|
|
|
...state.indexMeta, |
429
|
|
|
status: "pending", |
430
|
|
|
pendingCount: state.indexMeta.pendingCount + 1, |
431
|
|
|
error: undefined, |
432
|
|
|
}, |
433
|
|
|
}; |
434
|
|
|
case ActionTypes.IndexFulfill: |
435
|
|
|
return { |
436
|
|
|
...state, |
437
|
|
|
indexMeta: { |
438
|
|
|
...state.indexMeta, |
439
|
|
|
status: state.indexMeta.pendingCount <= 1 ? "fulfilled" : "pending", |
440
|
|
|
pendingCount: decrement(state.indexMeta.pendingCount), |
441
|
|
|
error: undefined, |
442
|
|
|
}, |
443
|
|
|
values: mergeIndexPayload(state.values, action.payload), |
444
|
|
|
}; |
445
|
|
|
case ActionTypes.IndexReject: |
446
|
|
|
return { |
447
|
|
|
...state, |
448
|
|
|
indexMeta: { |
449
|
|
|
...state.indexMeta, |
450
|
|
|
status: state.indexMeta.pendingCount <= 1 ? "rejected" : "pending", |
451
|
|
|
pendingCount: decrement(state.indexMeta.pendingCount), |
452
|
|
|
error: action.payload, |
453
|
|
|
}, |
454
|
|
|
}; |
455
|
|
|
case ActionTypes.CreateStart: |
456
|
|
|
// TODO: We could add an optimistic update here. |
457
|
|
|
return { |
458
|
|
|
...state, |
459
|
|
|
createMeta: { |
460
|
|
|
...state.createMeta, |
461
|
|
|
status: "pending", |
462
|
|
|
pendingCount: state.createMeta.pendingCount + 1, |
463
|
|
|
error: undefined, |
464
|
|
|
}, |
465
|
|
|
}; |
466
|
|
|
case ActionTypes.CreateFulfill: |
467
|
|
|
return { |
468
|
|
|
...state, |
469
|
|
|
createMeta: { |
470
|
|
|
status: state.createMeta.pendingCount <= 1 ? "fulfilled" : "pending", |
471
|
|
|
pendingCount: decrement(state.createMeta.pendingCount), |
472
|
|
|
error: undefined, |
473
|
|
|
}, |
474
|
|
|
values: mergeCreatePayload(state.values, action.payload), |
475
|
|
|
}; |
476
|
|
|
case ActionTypes.CreateReject: |
477
|
|
|
return { |
478
|
|
|
...state, |
479
|
|
|
createMeta: { |
480
|
|
|
status: state.createMeta.pendingCount <= 1 ? "rejected" : "pending", |
481
|
|
|
pendingCount: decrement(state.createMeta.pendingCount), |
482
|
|
|
error: action.payload, |
483
|
|
|
}, |
484
|
|
|
}; |
485
|
|
|
case ActionTypes.UpdateStart: |
486
|
|
|
return { |
487
|
|
|
...state, |
488
|
|
|
values: mergeUpdateStart(state.values, action), |
489
|
|
|
}; |
490
|
|
|
case ActionTypes.UpdateFulfill: |
491
|
|
|
return { |
492
|
|
|
...state, |
493
|
|
|
values: mergeUpdateFulfill(state.values, action), |
494
|
|
|
}; |
495
|
|
|
case ActionTypes.UpdateReject: |
496
|
|
|
return { |
497
|
|
|
...state, |
498
|
|
|
values: mergeUpdateReject(state.values, action), |
499
|
|
|
}; |
500
|
|
|
case ActionTypes.DeleteStart: |
501
|
|
|
return { |
502
|
|
|
...state, |
503
|
|
|
values: mergeDeleteStart(state.values, action), |
504
|
|
|
}; |
505
|
|
|
case ActionTypes.DeleteFulfill: |
506
|
|
|
return { |
507
|
|
|
...state, |
508
|
|
|
values: mergeDeleteFulfill(state.values, action), |
509
|
|
|
}; |
510
|
|
|
case ActionTypes.DeleteReject: |
511
|
|
|
return { |
512
|
|
|
...state, |
513
|
|
|
values: mergeDeleteReject(state.values, action), |
514
|
|
|
}; |
515
|
|
|
|
516
|
|
|
default: |
517
|
|
|
return state; |
518
|
|
|
} |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
export default reducer; |
522
|
|
|
|