UNPKG

5.18 kBPlain TextView Raw
1import type {
2 EntityState,
3 EntityStateAdapter,
4 IdSelector,
5 Update,
6 EntityId,
7} from './models'
8import {
9 createStateOperator,
10 createSingleArgumentStateOperator,
11} from './state_adapter'
12import {
13 selectIdValue,
14 ensureEntitiesArray,
15 splitAddedUpdatedEntities,
16} from './utils'
17
18export function createUnsortedStateAdapter<T>(
19 selectId: IdSelector<T>
20): EntityStateAdapter<T> {
21 type R = EntityState<T>
22
23 function addOneMutably(entity: T, state: R): void {
24 const key = selectIdValue(entity, selectId)
25
26 if (key in state.entities) {
27 return
28 }
29
30 state.ids.push(key)
31 state.entities[key] = entity
32 }
33
34 function addManyMutably(
35 newEntities: readonly T[] | Record<EntityId, T>,
36 state: R
37 ): void {
38 newEntities = ensureEntitiesArray(newEntities)
39
40 for (const entity of newEntities) {
41 addOneMutably(entity, state)
42 }
43 }
44
45 function setOneMutably(entity: T, state: R): void {
46 const key = selectIdValue(entity, selectId)
47 if (!(key in state.entities)) {
48 state.ids.push(key)
49 }
50 state.entities[key] = entity
51 }
52
53 function setManyMutably(
54 newEntities: readonly T[] | Record<EntityId, T>,
55 state: R
56 ): void {
57 newEntities = ensureEntitiesArray(newEntities)
58 for (const entity of newEntities) {
59 setOneMutably(entity, state)
60 }
61 }
62
63 function setAllMutably(
64 newEntities: readonly T[] | Record<EntityId, T>,
65 state: R
66 ): void {
67 newEntities = ensureEntitiesArray(newEntities)
68
69 state.ids = []
70 state.entities = {}
71
72 addManyMutably(newEntities, state)
73 }
74
75 function removeOneMutably(key: EntityId, state: R): void {
76 return removeManyMutably([key], state)
77 }
78
79 function removeManyMutably(keys: readonly EntityId[], state: R): void {
80 let didMutate = false
81
82 keys.forEach((key) => {
83 if (key in state.entities) {
84 delete state.entities[key]
85 didMutate = true
86 }
87 })
88
89 if (didMutate) {
90 state.ids = state.ids.filter((id) => id in state.entities)
91 }
92 }
93
94 function removeAllMutably(state: R): void {
95 Object.assign(state, {
96 ids: [],
97 entities: {},
98 })
99 }
100
101 function takeNewKey(
102 keys: { [id: string]: EntityId },
103 update: Update<T>,
104 state: R
105 ): boolean {
106 const original = state.entities[update.id]
107 const updated: T = Object.assign({}, original, update.changes)
108 const newKey = selectIdValue(updated, selectId)
109 const hasNewKey = newKey !== update.id
110
111 if (hasNewKey) {
112 keys[update.id] = newKey
113 delete state.entities[update.id]
114 }
115
116 state.entities[newKey] = updated
117
118 return hasNewKey
119 }
120
121 function updateOneMutably(update: Update<T>, state: R): void {
122 return updateManyMutably([update], state)
123 }
124
125 function updateManyMutably(
126 updates: ReadonlyArray<Update<T>>,
127 state: R
128 ): void {
129 const newKeys: { [id: string]: EntityId } = {}
130
131 const updatesPerEntity: { [id: string]: Update<T> } = {}
132
133 updates.forEach((update) => {
134 // Only apply updates to entities that currently exist
135 if (update.id in state.entities) {
136 // If there are multiple updates to one entity, merge them together
137 updatesPerEntity[update.id] = {
138 id: update.id,
139 // Spreads ignore falsy values, so this works even if there isn't
140 // an existing update already at this key
141 changes: {
142 ...(updatesPerEntity[update.id]
143 ? updatesPerEntity[update.id].changes
144 : null),
145 ...update.changes,
146 },
147 }
148 }
149 })
150
151 updates = Object.values(updatesPerEntity)
152
153 const didMutateEntities = updates.length > 0
154
155 if (didMutateEntities) {
156 const didMutateIds =
157 updates.filter((update) => takeNewKey(newKeys, update, state)).length >
158 0
159
160 if (didMutateIds) {
161 state.ids = state.ids.map((id) => newKeys[id] || id)
162 }
163 }
164 }
165
166 function upsertOneMutably(entity: T, state: R): void {
167 return upsertManyMutably([entity], state)
168 }
169
170 function upsertManyMutably(
171 newEntities: readonly T[] | Record<EntityId, T>,
172 state: R
173 ): void {
174 const [added, updated] = splitAddedUpdatedEntities<T>(
175 newEntities,
176 selectId,
177 state
178 )
179
180 updateManyMutably(updated, state)
181 addManyMutably(added, state)
182 }
183
184 return {
185 removeAll: createSingleArgumentStateOperator(removeAllMutably),
186 addOne: createStateOperator(addOneMutably),
187 addMany: createStateOperator(addManyMutably),
188 setOne: createStateOperator(setOneMutably),
189 setMany: createStateOperator(setManyMutably),
190 setAll: createStateOperator(setAllMutably),
191 updateOne: createStateOperator(updateOneMutably),
192 updateMany: createStateOperator(updateManyMutably),
193 upsertOne: createStateOperator(upsertOneMutably),
194 upsertMany: createStateOperator(upsertManyMutably),
195 removeOne: createStateOperator(removeOneMutably),
196 removeMany: createStateOperator(removeManyMutably),
197 }
198}