riducers 1.2.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +313 -19
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/esm/index.js +14 -0
- package/esm/index.js.map +1 -1
- package/package.json +8 -8
- package/src/helpers.ts +27 -0
- package/src/index.ts +208 -192
- package/src/listReducers.ts +86 -0
- package/src/mapListReducers.ts +193 -0
- package/src/mapReducers.ts +41 -0
- package/src/objReducers.ts +11 -0
- package/src/types.ts +55 -0
package/src/index.ts
CHANGED
|
@@ -1,228 +1,244 @@
|
|
|
1
|
-
|
|
1
|
+
import { ReducerOpts, ReducerAction, ModelObj, SortFn } from "./types";
|
|
2
|
+
import { mapInsertReducer, mapDeleteReducer, mapReplaceReducer } from "./mapReducers";
|
|
3
|
+
import {
|
|
4
|
+
listInsertReducer,
|
|
5
|
+
listUnshiftReducer,
|
|
6
|
+
listSortReducer,
|
|
7
|
+
listDeleteReducer,
|
|
8
|
+
listShiftReducer,
|
|
9
|
+
listReplaceReducer
|
|
10
|
+
} from "./listReducers";
|
|
11
|
+
import {
|
|
12
|
+
mapListInsertReducer,
|
|
13
|
+
mapListDeleteReducer,
|
|
14
|
+
mapListReplaceReducer,
|
|
15
|
+
mapListSortReducer,
|
|
16
|
+
mapListShiftReducer,
|
|
17
|
+
mapListUnshiftReducer,
|
|
18
|
+
MapListState
|
|
19
|
+
} from "./mapListReducers";
|
|
20
|
+
import { objInsertReducer, objReplaceReducer, objDeleteReducer } from "./objReducers";
|
|
21
|
+
|
|
22
|
+
// Re-export types for external usage
|
|
23
|
+
export * from "./types";
|
|
24
|
+
export { keySort } from "./helpers";
|
|
2
25
|
|
|
3
|
-
|
|
4
|
-
[key: string]: any;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
type SortFn = (a: ModelObj, b: ModelObj) => number;
|
|
8
|
-
|
|
9
|
-
interface ListAction {
|
|
10
|
-
payload: ModelObj | ModelObj[];
|
|
11
|
-
sort?: SortFn;
|
|
12
|
-
keyName: string;
|
|
13
|
-
mapKeyName?: string;
|
|
14
|
-
isMapList: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface MapAction {
|
|
18
|
-
payload: ModelObj | ModelObj[];
|
|
19
|
-
sort?: SortFn;
|
|
20
|
-
keyName: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface ListSortAction {
|
|
24
|
-
sort?: SortFn;
|
|
25
|
-
keyName: string;
|
|
26
|
-
mapKeyName?: string;
|
|
27
|
-
isMapList: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const keySort = (key: string) => (a: ModelObj, b: ModelObj) => {
|
|
31
|
-
if (typeof a[key] === "number" && typeof b[key] === "number") return a[key] - b[key];
|
|
32
|
-
else if (typeof a[key] === "string" && typeof b[key] === "string") return a[key].localeCompare(b[key]);
|
|
33
|
-
return 0;
|
|
34
|
-
};
|
|
26
|
+
const OPS = ["insert", "replace", "delete", "clear", "sort"];
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Gets the default initial state based on state type
|
|
30
|
+
*/
|
|
31
|
+
const getDefaultInitialState = (stateType: string): any => {
|
|
32
|
+
switch (stateType) {
|
|
33
|
+
case "list":
|
|
34
|
+
return [];
|
|
35
|
+
case "map":
|
|
36
|
+
return {};
|
|
37
|
+
default:
|
|
38
|
+
return null;
|
|
42
39
|
}
|
|
43
|
-
return newState;
|
|
44
40
|
};
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Handles map-specific operation normalization
|
|
44
|
+
* When using map type, allows using the key as the operation (e.g., "users/123" for insert/delete)
|
|
45
|
+
*/
|
|
46
|
+
const normalizeMapOperation = (
|
|
47
|
+
op: string,
|
|
48
|
+
payload: any,
|
|
49
|
+
keyName: string
|
|
50
|
+
): { op: string; payload: any } => {
|
|
51
|
+
if (!OPS.includes(op)) {
|
|
52
|
+
if (payload && !Array.isArray(payload)) {
|
|
53
|
+
payload[keyName] = op;
|
|
54
|
+
return { op: "insert", payload };
|
|
55
|
+
}
|
|
56
|
+
if (!payload) {
|
|
57
|
+
const newPayload: ModelObj = { [keyName]: op };
|
|
58
|
+
return { op: "delete", payload: newPayload };
|
|
59
|
+
}
|
|
52
60
|
}
|
|
53
|
-
return
|
|
61
|
+
return { op, payload };
|
|
54
62
|
};
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Handles list operations (insert, replace, delete, shift, sort)
|
|
66
|
+
*/
|
|
67
|
+
const handleListOperation = (
|
|
68
|
+
state: ModelObj[],
|
|
69
|
+
op: string,
|
|
70
|
+
payload: any,
|
|
71
|
+
sort: SortFn | undefined,
|
|
72
|
+
keyName: string,
|
|
73
|
+
initialState: any
|
|
74
|
+
): ModelObj[] | undefined => {
|
|
75
|
+
const isMapList = false;
|
|
76
|
+
switch (op) {
|
|
77
|
+
case "insert":
|
|
78
|
+
if (payload) return listInsertReducer(state, { payload, sort, keyName, isMapList });
|
|
79
|
+
break;
|
|
80
|
+
case "unshift":
|
|
81
|
+
if (payload) return listUnshiftReducer(state, { payload, sort, keyName, isMapList });
|
|
82
|
+
break;
|
|
83
|
+
case "replace":
|
|
84
|
+
return listReplaceReducer(state, { payload, sort, keyName, isMapList });
|
|
85
|
+
case "delete":
|
|
86
|
+
return listDeleteReducer(state, { payload, keyName, isMapList });
|
|
87
|
+
case "shift":
|
|
88
|
+
return listShiftReducer(state);
|
|
89
|
+
case "clear":
|
|
90
|
+
return initialState;
|
|
91
|
+
case "sort":
|
|
92
|
+
return listSortReducer(state, { sort, keyName, isMapList });
|
|
61
93
|
}
|
|
62
|
-
return
|
|
94
|
+
return undefined;
|
|
63
95
|
};
|
|
64
96
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Handles mapList operations (insert, replace, delete, shift, sort, unshift)
|
|
99
|
+
* mapList is a hybrid: { [mapKeyValue]: ModelObj[] }
|
|
100
|
+
*/
|
|
101
|
+
const handleMapListOperation = (
|
|
102
|
+
state: MapListState,
|
|
103
|
+
op: string,
|
|
104
|
+
payload: any,
|
|
105
|
+
sort: SortFn | undefined,
|
|
106
|
+
keyName: string,
|
|
107
|
+
mapKeyName: string,
|
|
108
|
+
initialState: any
|
|
109
|
+
): MapListState | undefined => {
|
|
110
|
+
switch (op) {
|
|
111
|
+
case "insert":
|
|
112
|
+
if (payload) return mapListInsertReducer(state, { payload, sort, keyName, mapKeyName });
|
|
113
|
+
break;
|
|
114
|
+
case "unshift":
|
|
115
|
+
if (payload) return mapListUnshiftReducer(state, { payload, sort, keyName, mapKeyName });
|
|
116
|
+
break;
|
|
117
|
+
case "replace":
|
|
118
|
+
return mapListReplaceReducer(state, { payload, sort, keyName, mapKeyName });
|
|
119
|
+
case "delete":
|
|
120
|
+
return mapListDeleteReducer(state, { payload, keyName, mapKeyName });
|
|
121
|
+
case "shift":
|
|
122
|
+
return mapListShiftReducer(state);
|
|
123
|
+
case "clear":
|
|
124
|
+
return initialState;
|
|
125
|
+
case "sort":
|
|
126
|
+
return mapListSortReducer(state, { sort, keyName, mapKeyName });
|
|
82
127
|
}
|
|
83
|
-
|
|
84
|
-
return newState;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const listSortReducer = (state: ModelObj[], action: ListSortAction) => {
|
|
88
|
-
if (!state) state = [];
|
|
89
|
-
let newState = [...state];
|
|
90
|
-
let sort = action?.sort ? action.sort : keySort(action.keyName);
|
|
91
|
-
newState.sort(sort);
|
|
92
|
-
return newState;
|
|
128
|
+
return undefined;
|
|
93
129
|
};
|
|
94
130
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Handles map operations (insert, replace, delete, clear)
|
|
133
|
+
*/
|
|
134
|
+
const handleMapOperation = (
|
|
135
|
+
state: ModelObj,
|
|
136
|
+
op: string,
|
|
137
|
+
payload: any,
|
|
138
|
+
keyName: string,
|
|
139
|
+
initialState: any
|
|
140
|
+
): ModelObj | null | undefined => {
|
|
141
|
+
switch (op) {
|
|
142
|
+
case "insert":
|
|
143
|
+
if (payload) return mapInsertReducer(state, { payload, keyName });
|
|
144
|
+
break;
|
|
145
|
+
case "replace":
|
|
146
|
+
return mapReplaceReducer(state, { payload, keyName });
|
|
147
|
+
case "delete":
|
|
148
|
+
return mapDeleteReducer(state, { payload, keyName });
|
|
149
|
+
case "clear":
|
|
150
|
+
return initialState;
|
|
104
151
|
}
|
|
105
|
-
return
|
|
152
|
+
return undefined;
|
|
106
153
|
};
|
|
107
154
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Handles static/object operations (insert, replace, delete, clear)
|
|
157
|
+
*/
|
|
158
|
+
const handleStaticOperation = (
|
|
159
|
+
state: any,
|
|
160
|
+
op: string,
|
|
161
|
+
payload: any,
|
|
162
|
+
initialState: any
|
|
163
|
+
): any | undefined => {
|
|
164
|
+
switch (op) {
|
|
165
|
+
case "insert":
|
|
166
|
+
return objInsertReducer(state, { payload });
|
|
167
|
+
case "replace":
|
|
168
|
+
return objReplaceReducer(state, { payload });
|
|
169
|
+
case "delete":
|
|
170
|
+
return objDeleteReducer();
|
|
171
|
+
case "clear":
|
|
172
|
+
return initialState;
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
124
175
|
};
|
|
125
176
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Builds a reducer function for managing state with various operations
|
|
179
|
+
*/
|
|
180
|
+
function reducerBuilder(key: string, opts?: ReducerOpts) {
|
|
181
|
+
const stateType = opts?.stateType ?? "static";
|
|
182
|
+
const isList = stateType === "list";
|
|
183
|
+
const isMap = stateType === "map";
|
|
184
|
+
const isMapList = stateType === "mapList";
|
|
129
185
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
186
|
+
if (isMapList && !opts?.mapKeyName) {
|
|
187
|
+
throw new Error("mapKeyName is required for stateType: mapList");
|
|
188
|
+
}
|
|
133
189
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
keyName?: string;
|
|
138
|
-
mapKeyName?: string;
|
|
139
|
-
initialState?: any;
|
|
140
|
-
}
|
|
190
|
+
// Use provided initialState if defined, otherwise get default based on state type
|
|
191
|
+
const initialState = opts?.initialState !== undefined ? opts.initialState : getDefaultInitialState(stateType);
|
|
192
|
+
const actionPattern = new RegExp(`^${key}/([^\/]+)$`);
|
|
141
193
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
let initialState = opts?.initialState;
|
|
145
|
-
let stateType = opts?.stateType ?? "static";
|
|
146
|
-
let isList = stateType === "list";
|
|
147
|
-
let isMap = stateType === "map";
|
|
148
|
-
let isMapList = stateType === "mapList";
|
|
149
|
-
|
|
150
|
-
if (isMapList && !opts?.mapKeyName) throw new Error("mapKeyName is required for stateType: mapList");
|
|
151
|
-
|
|
152
|
-
if (initialState === undefined) {
|
|
153
|
-
if (isList) initialState = [];
|
|
154
|
-
else if (isMap) initialState = {};
|
|
155
|
-
else initialState = null;
|
|
156
|
-
}
|
|
157
|
-
return (state: any, action: { type: string; payload?: any; sort?: (a: ModelObj, b: ModelObj) => number }) => {
|
|
194
|
+
return (state: any, action: ReducerAction): any => {
|
|
195
|
+
// Initialize state
|
|
158
196
|
if (state === undefined) {
|
|
159
|
-
|
|
160
|
-
else if (isList) return [];
|
|
161
|
-
else if (isMap) return {};
|
|
162
|
-
else if (isMapList) return {};
|
|
163
|
-
else return null;
|
|
197
|
+
return initialState !== undefined ? initialState : getDefaultInitialState(stateType);
|
|
164
198
|
}
|
|
165
|
-
let rx = new RegExp(`^${key}/([^\/]+)$`);
|
|
166
|
-
let m = action.type.match(rx);
|
|
167
|
-
if (!m) return state;
|
|
168
199
|
|
|
169
|
-
|
|
200
|
+
// Match action type
|
|
201
|
+
const match = action.type.match(actionPattern);
|
|
202
|
+
if (!match) return state;
|
|
203
|
+
|
|
204
|
+
let op = match[1];
|
|
170
205
|
let payload = action.payload;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
payload = {};
|
|
181
|
-
payload[keyName] = op;
|
|
182
|
-
op = "delete";
|
|
183
|
-
}
|
|
206
|
+
const keyName = opts?.keyName ?? "id";
|
|
207
|
+
const mapKeyName = opts?.mapKeyName;
|
|
208
|
+
const sort = action.sort ?? opts?.sort;
|
|
209
|
+
|
|
210
|
+
// Handle map-specific operation normalization
|
|
211
|
+
if (isMap) {
|
|
212
|
+
const normalized = normalizeMapOperation(op, payload, keyName);
|
|
213
|
+
op = normalized.op;
|
|
214
|
+
payload = normalized.payload;
|
|
184
215
|
}
|
|
185
216
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if ((isList || isMapList) && payload) return listInsertReducer(state, { payload, sort, keyName, mapKeyName, isMapList });
|
|
190
|
-
else if (isMap && payload) return mapInsertReducer(state, { payload, keyName });
|
|
191
|
-
return objInsertReducer(state, { payload });
|
|
192
|
-
|
|
193
|
-
} else if (op === "replace") {
|
|
194
|
-
|
|
195
|
-
if (isList || isMapList) return listReplaceReducer(state, { payload, sort, keyName, mapKeyName, isMapList });
|
|
196
|
-
else if (isMap) return mapReplaceReducer(state, { payload, keyName });
|
|
197
|
-
return objReplaceReducer(state, { payload });
|
|
198
|
-
|
|
199
|
-
} else if (op === "delete") {
|
|
200
|
-
|
|
201
|
-
if (isList || isMapList) return listDeleteReducer(state, { payload, keyName, mapKeyName, isMapList });
|
|
202
|
-
else if (isMap) return mapDeleteReducer(state, { payload, keyName });
|
|
203
|
-
return objDeleteReducer();
|
|
204
|
-
|
|
205
|
-
} else if (op === "shift") {
|
|
206
|
-
if (isList || isMapList) return listShiftReducer(state);
|
|
207
|
-
else {
|
|
208
|
-
console.error(`unshift is only valid for stateType: list or mapList`);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
} else if (op === "clear") {
|
|
212
|
-
|
|
213
|
-
return initialState;
|
|
214
|
-
|
|
215
|
-
} else if (op === "sort") {
|
|
216
|
-
|
|
217
|
-
if (isList || isMapList) return listSortReducer(state, { sort, keyName, mapKeyName, isMapList });
|
|
217
|
+
// Handle operations based on state type
|
|
218
|
+
let result: any;
|
|
218
219
|
|
|
220
|
+
if (isList) {
|
|
221
|
+
result = handleListOperation(state, op, payload, sort, keyName, initialState);
|
|
222
|
+
} else if (isMapList) {
|
|
223
|
+
result = handleMapListOperation(state, op, payload, sort, keyName, mapKeyName!, initialState);
|
|
224
|
+
} else if (isMap) {
|
|
225
|
+
result = handleMapOperation(state, op, payload, keyName, initialState);
|
|
219
226
|
} else {
|
|
227
|
+
result = handleStaticOperation(state, op, payload, initialState);
|
|
228
|
+
}
|
|
220
229
|
|
|
221
|
-
|
|
230
|
+
if (result !== undefined) {
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
222
233
|
|
|
234
|
+
// Unknown operation
|
|
235
|
+
if (!["insert", "unshift", "replace", "delete", "shift", "clear", "sort"].includes(op)) {
|
|
236
|
+
console.error(
|
|
237
|
+
`unknown op (${op}) in action type: ${action.type}. Op should be one of: insert, delete, replace, clear, or sort.`
|
|
238
|
+
);
|
|
223
239
|
}
|
|
224
|
-
|
|
225
|
-
return state || (
|
|
240
|
+
|
|
241
|
+
return state || getDefaultInitialState(stateType);
|
|
226
242
|
};
|
|
227
243
|
}
|
|
228
244
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ModelObj, Id, ListAction, ListSortAction } from "./types";
|
|
2
|
+
import { normalizePayload, initializeState, keySort } from "./helpers";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Helper to get key mappings from items
|
|
6
|
+
*/
|
|
7
|
+
const getKeyMap = (items: ModelObj[], keyName: string): Id[] => {
|
|
8
|
+
return items.map((item: ModelObj) => item[keyName]);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Helper to find items to insert (not in state) and items to replace (in state)
|
|
13
|
+
*/
|
|
14
|
+
const partitionItems = (respMap: Id[], stateMap: Id[]) => {
|
|
15
|
+
const toInsert = respMap.filter((id: Id) => stateMap.indexOf(id) < 0);
|
|
16
|
+
const toReplace = respMap.filter((id: Id) => stateMap.indexOf(id) >= 0);
|
|
17
|
+
return { toInsert, toReplace };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const listInsertReducer = (state: ModelObj[], action: ListAction): ModelObj[] => {
|
|
21
|
+
const currentState = initializeState(state, []);
|
|
22
|
+
const items = normalizePayload(action.payload);
|
|
23
|
+
|
|
24
|
+
let newState = [...currentState];
|
|
25
|
+
const respMap = getKeyMap(items, action.keyName);
|
|
26
|
+
const stateMap = getKeyMap(newState, action.keyName);
|
|
27
|
+
const { toInsert, toReplace } = partitionItems(respMap, stateMap);
|
|
28
|
+
|
|
29
|
+
// Update existing items
|
|
30
|
+
for (const id of toReplace) {
|
|
31
|
+
const stateIndex = stateMap.indexOf(id);
|
|
32
|
+
const respIndex = respMap.indexOf(id);
|
|
33
|
+
newState[stateIndex] = { ...items[respIndex] };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Add new items
|
|
37
|
+
if (toInsert.length > 0) {
|
|
38
|
+
const inserts = toInsert.map((id: Id) => items[respMap.indexOf(id)]);
|
|
39
|
+
newState = [...newState, ...inserts];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (action?.sort) {
|
|
43
|
+
newState.sort(action.sort);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return newState;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const listUnshiftReducer = (state: ModelObj[], action: ListAction): ModelObj[] => {
|
|
50
|
+
const currentState = initializeState(state, []);
|
|
51
|
+
const items = normalizePayload(action.payload);
|
|
52
|
+
return [...items, ...currentState];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const listSortReducer = (state: ModelObj[], action: ListSortAction): ModelObj[] => {
|
|
56
|
+
const currentState = initializeState(state, []);
|
|
57
|
+
const newState = [...currentState];
|
|
58
|
+
const sort = action?.sort ?? keySort(action.keyName);
|
|
59
|
+
newState.sort(sort);
|
|
60
|
+
return newState;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const listDeleteReducer = (state: ModelObj[], action: ListAction): ModelObj[] => {
|
|
64
|
+
const currentState = !state || !Array.isArray(state) ? [] : state;
|
|
65
|
+
const items = normalizePayload(action.payload);
|
|
66
|
+
const deleteKeys = new Set<Id>(getKeyMap(items, action.keyName));
|
|
67
|
+
return currentState.filter((item: ModelObj) => !deleteKeys.has(item[action.keyName]));
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const listShiftReducer = (state: ModelObj[]): ModelObj[] => {
|
|
71
|
+
const currentState = !state || !Array.isArray(state) ? [] : state;
|
|
72
|
+
const newState = [...currentState];
|
|
73
|
+
newState.shift();
|
|
74
|
+
return newState;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const listReplaceReducer = (state: ModelObj[], action: ListAction): ModelObj[] => {
|
|
78
|
+
const items = normalizePayload(action.payload);
|
|
79
|
+
const result = items.map((item: ModelObj) => ({ ...item }));
|
|
80
|
+
|
|
81
|
+
if (action?.sort) {
|
|
82
|
+
result.sort(action.sort);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
};
|