Skip to content

Commit b01916c

Browse files
authored
Merge pull request #2464 from reduxjs/bugfix/entity-adapter-sorting
2 parents 9e24958 + 75aced4 commit b01916c

File tree

2 files changed

+68
-25
lines changed

2 files changed

+68
-25
lines changed

packages/toolkit/src/entities/sorted_state_adapter.ts

+22-21
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

+46-4
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' }
@@ -652,12 +680,20 @@ describe('Sorted State Adapter', () => {
652680
test('updateMany', () => {
653681
const firstChange = { title: 'First Change' }
654682
const secondChange = { title: 'Second Change' }
655-
const withMany = adapter.setAll(state, [TheGreatGatsby, AClockworkOrange])
683+
const thirdChange = { title: 'Third Change' }
684+
const fourthChange = { author: 'Fourth Change' }
685+
const withMany = adapter.setAll(state, [
686+
TheGreatGatsby,
687+
AClockworkOrange,
688+
TheHobbit,
689+
])
656690

657691
const result = createNextState(withMany, (draft) => {
658692
adapter.updateMany(draft, [
659-
{ id: TheGreatGatsby.id, changes: firstChange },
660-
{ id: AClockworkOrange.id, changes: secondChange },
693+
{ id: TheHobbit.id, changes: firstChange },
694+
{ id: TheGreatGatsby.id, changes: secondChange },
695+
{ id: AClockworkOrange.id, changes: thirdChange },
696+
{ id: TheHobbit.id, changes: fourthChange },
661697
])
662698
})
663699

@@ -666,14 +702,20 @@ describe('Sorted State Adapter', () => {
666702
"entities": Object {
667703
"aco": Object {
668704
"id": "aco",
669-
"title": "Second Change",
705+
"title": "Third Change",
670706
},
671707
"tgg": Object {
672708
"id": "tgg",
709+
"title": "Second Change",
710+
},
711+
"th": Object {
712+
"author": "Fourth Change",
713+
"id": "th",
673714
"title": "First Change",
674715
},
675716
},
676717
"ids": Array [
718+
"th",
677719
"tgg",
678720
"aco",
679721
],

0 commit comments

Comments
 (0)