Skip to content

Commit 93cd136

Browse files
committed
Rewrite updateMany to ensure stable sorting order
1 parent 9e24958 commit 93cd136

File tree

2 files changed

+50
-21
lines changed

2 files changed

+50
-21
lines changed

packages/toolkit/src/entities/sorted_state_adapter.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,33 +71,30 @@ export function createSortedStateAdapter<T>(
7171
return updateManyMutably([update], state)
7272
}
7373

74-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
75-
function takeUpdatedModel(models: T[], update: Update<T>, state: R): boolean {
76-
if (!(update.id in state.entities)) {
77-
return false
78-
}
79-
80-
const original = state.entities[update.id]
81-
const updated = Object.assign({}, original, update.changes)
82-
const newKey = selectIdValue(updated, selectId)
83-
84-
delete state.entities[update.id]
85-
86-
models.push(updated)
87-
88-
return newKey !== update.id
89-
}
90-
9174
function updateManyMutably(
9275
updates: ReadonlyArray<Update<T>>,
9376
state: R
9477
): void {
95-
const models: T[] = []
78+
let appliedUpdates = false
79+
80+
for (let update of updates) {
81+
const entity = state.entities[update.id]
82+
if (!entity) {
83+
continue
84+
}
9685

97-
updates.forEach((update) => takeUpdatedModel(models, update, state))
86+
appliedUpdates = true
9887

99-
if (models.length !== 0) {
100-
merge(models, state)
88+
Object.assign(entity, update.changes)
89+
const newId = selectId(entity)
90+
if (update.id !== newId) {
91+
delete state.entities[update.id]
92+
state.entities[newId] = entity
93+
}
94+
}
95+
96+
if (appliedUpdates) {
97+
resortEntities(state)
10198
}
10299
}
103100

@@ -139,6 +136,10 @@ export function createSortedStateAdapter<T>(
139136
state.entities[selectId(model)] = model
140137
})
141138

139+
resortEntities(state)
140+
}
141+
142+
function resortEntities(state: R) {
142143
const allEntities = Object.values(state.entities) as T[]
143144
allEntities.sort(sort)
144145

packages/toolkit/src/entities/tests/sorted_state_adapter.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,34 @@ describe('Sorted State Adapter', () => {
343343
})
344344
})
345345

346+
it('should maintain a stable sorting order when updating items', () => {
347+
interface OrderedEntity {
348+
id: string
349+
order: number
350+
ts: number
351+
}
352+
const sortedItemsAdapter = createEntityAdapter<OrderedEntity>({
353+
sortComparer: (a, b) => a.order - b.order,
354+
})
355+
const withInitialItems = sortedItemsAdapter.setAll(
356+
sortedItemsAdapter.getInitialState(),
357+
[
358+
{ id: 'A', order: 1, ts: 0 },
359+
{ id: 'B', order: 2, ts: 0 },
360+
{ id: 'C', order: 3, ts: 0 },
361+
{ id: 'D', order: 3, ts: 0 },
362+
{ id: 'E', order: 3, ts: 0 },
363+
]
364+
)
365+
366+
const updated = sortedItemsAdapter.updateOne(withInitialItems, {
367+
id: 'C',
368+
changes: { ts: 5 },
369+
})
370+
371+
expect(updated.ids).toEqual(['A', 'B', 'C', 'D', 'E'])
372+
})
373+
346374
it('should let you update many entities by id in the state', () => {
347375
const firstChange = { title: 'Zack' }
348376
const secondChange = { title: 'Aaron' }

0 commit comments

Comments
 (0)