Repository: reduxjs/redux-toolkit (ts)

Issue:
`updateMany` ignores early updates if later updates have the same ID
The `updateMany` action generated by the `createEntityAdapter` function is not working as intended when there are multiple updates to the same entity.

Consider the following code:

```ts
const slice = createSlice({
  name: 'books',
  initialState: booksAdapter.getInitialState(),
  reducers: {
    bookAdd: adapter.addOne,
    bookUpdates: adapter.updateMany,
  },
});

// configure store and add books ...

store.dispatch(
  slice.actions.bookUpdates([
    { id: 1, changes: { title: 'New Title 1' } },
    { id: 2, changes: { title: 'New Title 2' } },
    { id: 1, changes: { author: 'New Author 1' } },
    { id: 2, changes: { author: 'New Author 2' } },
  ])
);
```

The outcome of that last statement should be that entity `1` has a title of "New Title 1" _and_ a new author of "New Author 1".  But what actually happens is that the `author` is changed but not the title.

The [docs](https://redux-toolkit.js.org/api/createEntityAdapter) say, "If `updateMany()` is called with multiple updates targeted to the same ID, they will be merged into a single update, with later updates overwriting the earlier ones."  That's a little ambiguous.  It could mean that the later updates should be merged with earlier updates (not conflicting if the fields are different), or that the later updates would completely replace earlier updates with the same ID.  It's doing the latter, but should do the former.

See the codesandbox here:

https://codesandbox.io/s/redux-toolkit-updatemany-test-verzl?file=/src/App.tsx

Pull request:
Fixed multiple updates with same id in updateMany
Fixes #619.

Patch:
diff --git a/src/entities/fixtures/book.ts b/src/entities/fixtures/book.ts
index 6191374a..8c0a8d5d 100644
--- a/src/entities/fixtures/book.ts
+++ b/src/entities/fixtures/book.ts
@@ -1,6 +1,7 @@
 export interface BookModel {
   id: string
   title: string
+  author?: string
 }
 
 export const AClockworkOrange: BookModel = Object.freeze({
@@ -17,3 +18,9 @@ export const TheGreatGatsby: BookModel = Object.freeze({
   id: 'tgg',
   title: 'The Great Gatsby'
 })
+
+export const TheHobbit: BookModel = Object.freeze({
+  id: 'th',
+  title: 'The Hobbit',
+  author: 'J. R. R. Tolkien'
+})
diff --git a/src/entities/unsorted_state_adapter.ts b/src/entities/unsorted_state_adapter.ts
index 13f20733..4fec5c1f 100644
--- a/src/entities/unsorted_state_adapter.ts
+++ b/src/entities/unsorted_state_adapter.ts
@@ -16,7 +16,7 @@ export function createUnsortedStateAdapter<T>(
 ): EntityStateAdapter<T> {
   type R = EntityState<T>
 
-  function addOneMutably(entity: T, state: EntityState<T>): void {
+  function addOneMutably(entity: T, state: R): void {
     const key = selectIdValue(entity, selectId)
 
     if (key in state.entities) {
@@ -108,10 +108,15 @@ export function createUnsortedStateAdapter<T>(
       if (update.id in state.entities) {
         // If there are multiple updates to one entity, merge them together
         updatesPerEntity[update.id] = {
+          id: update.id,
           // Spreads ignore falsy values, so this works even if there isn't
           // an existing update already at this key
-          ...updatesPerEntity[update.id],
-          ...update
+          changes: {
+            ...(updatesPerEntity[update.id]
+              ? updatesPerEntity[update.id].changes
+              : null),
+            ...update.changes
+          }
         }
       }
     })


Test patch:
diff --git a/src/entities/unsorted_state_adapter.test.ts b/src/entities/unsorted_state_adapter.test.ts
index 44f5d696..3e4521b5 100644
--- a/src/entities/unsorted_state_adapter.test.ts
+++ b/src/entities/unsorted_state_adapter.test.ts
@@ -4,7 +4,8 @@ import {
   BookModel,
   TheGreatGatsby,
   AClockworkOrange,
-  AnimalFarm
+  AnimalFarm,
+  TheHobbit
 } from './fixtures/book'
 import { createNextState } from '..'
 
@@ -466,12 +467,20 @@ describe('Unsorted State Adapter', () => {
     test('updateMany', () => {
       const firstChange = { title: 'First Change' }
       const secondChange = { title: 'Second Change' }
-      const withMany = adapter.setAll(state, [TheGreatGatsby, AClockworkOrange])
+      const thirdChange = { title: 'Third Change' }
+      const fourthChange = { author: 'Fourth Change' }
+      const withMany = adapter.setAll(state, [
+        TheGreatGatsby,
+        AClockworkOrange,
+        TheHobbit
+      ])
 
       const result = createNextState(withMany, draft => {
         adapter.updateMany(draft, [
-          { id: TheGreatGatsby.id, changes: firstChange },
-          { id: AClockworkOrange.id, changes: secondChange }
+          { id: TheHobbit.id, changes: firstChange },
+          { id: TheGreatGatsby.id, changes: secondChange },
+          { id: AClockworkOrange.id, changes: thirdChange },
+          { id: TheHobbit.id, changes: fourthChange }
         ])
       })
 
@@ -480,16 +489,22 @@ describe('Unsorted State Adapter', () => {
           "entities": Object {
             "aco": Object {
               "id": "aco",
-              "title": "Second Change",
+              "title": "Third Change",
             },
             "tgg": Object {
               "id": "tgg",
+              "title": "Second Change",
+            },
+            "th": Object {
+              "author": "Fourth Change",
+              "id": "th",
               "title": "First Change",
             },
           },
           "ids": Array [
             "tgg",
             "aco",
+            "th",
           ],
         }
       `)