diff --git a/modules/signals/entities/spec/updaters/update-all-entities.spec.ts b/modules/signals/entities/spec/updaters/update-all-entities.spec.ts index 1324147461..9af16ed13e 100644 --- a/modules/signals/entities/spec/updaters/update-all-entities.spec.ts +++ b/modules/signals/entities/spec/updaters/update-all-entities.spec.ts @@ -51,8 +51,10 @@ describe('updateAllEntities', () => { patchState( store, - updateAllEntities({ text: '' }), - updateAllEntities((todo) => ({ completed: !todo.completed })) + updateAllEntities({ text: '' }, { selectId: (todo) => todo._id }), + updateAllEntities((todo) => ({ completed: !todo.completed }), { + selectId: (todo) => todo._id, + }) ); expect(store.entityMap()).toBe(entityMap); @@ -79,7 +81,13 @@ describe('updateAllEntities', () => { collection: 'todo', selectId: selectTodoId, }), - updateAllEntities({ completed: false }, { collection: 'todo' }) + updateAllEntities( + { completed: false }, + { + collection: 'todo', + selectId: (todo) => todo._id, + } + ) ); expect(store.todoEntityMap()).toEqual({ @@ -98,6 +106,7 @@ describe('updateAllEntities', () => { store, updateAllEntities(({ completed }) => ({ completed: !completed }), { collection: 'todo', + selectId: (todo) => todo._id, }) ); diff --git a/modules/signals/entities/spec/updaters/update-entities.spec.ts b/modules/signals/entities/spec/updaters/update-entities.spec.ts index b5ce41e28c..13a4f794a1 100644 --- a/modules/signals/entities/spec/updaters/update-entities.spec.ts +++ b/modules/signals/entities/spec/updaters/update-entities.spec.ts @@ -50,14 +50,20 @@ describe('updateEntities', () => { patchState( store, - updateEntities({ - predicate: (todo) => todo.text.startsWith('Buy'), - changes: { completed: false }, - }), - updateEntities({ - predicate: ({ completed }) => !completed, - changes: ({ text }) => ({ text: `Don't ${text}` }), - }) + updateEntities( + { + predicate: (todo) => todo.text.startsWith('Buy'), + changes: { completed: false }, + }, + { selectId: (todo) => todo._id } + ), + updateEntities( + { + predicate: ({ completed }) => !completed, + changes: ({ text }) => ({ text: `Don't ${text}` }), + }, + { selectId: (todo) => todo._id } + ) ); expect(store.entityMap()).toEqual({ @@ -228,4 +234,229 @@ describe('updateEntities', () => { expect(store.todoIds()).toEqual(['x', 'y', 'z']); expect(store.todoEntities()).toEqual([todo1, todo2, todo3]); }); + + it('updates entity ids', () => { + const Store = signalStore(withEntities()); + const store = new Store(); + + patchState( + store, + addEntities([user1, user2, user3]), + updateEntities({ + ids: [user1.id, user2.id], + changes: ({ id }) => ({ id: id + 10, firstName: `Jimmy${id}` }), + }), + updateEntities({ + ids: [user3.id], + changes: { id: 303, lastName: 'Hendrix' }, + }) + ); + + expect(store.entityMap()).toEqual({ + 11: { ...user1, id: 11, firstName: 'Jimmy1' }, + 12: { ...user2, id: 12, firstName: 'Jimmy2' }, + 303: { ...user3, id: 303, lastName: 'Hendrix' }, + }); + expect(store.ids()).toEqual([11, 12, 303]); + + patchState( + store, + updateEntities({ + predicate: ({ id }) => id > 300, + changes: ({ id }) => ({ id: id - 300 }), + }), + updateEntities({ + predicate: ({ firstName }) => firstName === 'Jimmy1', + changes: { id: 1, firstName: 'Jimmy' }, + }) + ); + + expect(store.entityMap()).toEqual({ + 1: { ...user1, id: 1, firstName: 'Jimmy' }, + 12: { ...user2, id: 12, firstName: 'Jimmy2' }, + 3: { ...user3, id: 3, lastName: 'Hendrix' }, + }); + expect(store.ids()).toEqual([1, 12, 3]); + }); + + it('updates custom entity ids', () => { + const Store = signalStore(withEntities()); + const store = new Store(); + + patchState( + store, + addEntities([todo1, todo2, todo3], { selectId: (todo) => todo._id }), + updateEntities( + { + ids: [todo1._id, todo2._id], + changes: ({ _id }) => ({ _id: _id + 10, text: `Todo ${_id}` }), + }, + { selectId: (todo) => todo._id } + ), + updateEntities( + { + ids: [todo3._id], + changes: { _id: 'z30' }, + }, + { selectId: (todo) => todo._id } + ) + ); + + expect(store.entityMap()).toEqual({ + x10: { ...todo1, _id: 'x10', text: 'Todo x' }, + y10: { ...todo2, _id: 'y10', text: 'Todo y' }, + z30: { ...todo3, _id: 'z30' }, + }); + expect(store.ids()).toEqual(['x10', 'y10', 'z30']); + + patchState( + store, + updateEntities( + { + predicate: ({ text }) => text.startsWith('Todo '), + changes: ({ _id }) => ({ _id: `${_id}0` }), + }, + { selectId: (todo) => todo._id } + ), + updateEntities( + { + predicate: ({ _id }) => _id === 'z30', + changes: { _id: 'z' }, + }, + { selectId: (todo) => todo._id } + ) + ); + + expect(store.entityMap()).toEqual({ + x100: { ...todo1, _id: 'x100', text: 'Todo x' }, + y100: { ...todo2, _id: 'y100', text: 'Todo y' }, + z: { ...todo3, _id: 'z' }, + }); + expect(store.ids()).toEqual(['x100', 'y100', 'z']); + }); + + it('updates entity ids from specified collection', () => { + const Store = signalStore( + withEntities({ + entity: type(), + collection: 'user', + }) + ); + const store = new Store(); + + patchState( + store, + addEntities([user1, user2, user3], { collection: 'user' }), + updateEntities( + { + ids: [user1.id, user2.id], + changes: ({ id }) => ({ id: id + 100, firstName: `Jimmy${id}` }), + }, + { collection: 'user' } + ), + updateEntities( + { + ids: [user3.id], + changes: { id: 303, lastName: 'Hendrix' }, + }, + { collection: 'user' } + ) + ); + + expect(store.userEntityMap()).toEqual({ + 101: { ...user1, id: 101, firstName: 'Jimmy1' }, + 102: { ...user2, id: 102, firstName: 'Jimmy2' }, + 303: { ...user3, id: 303, lastName: 'Hendrix' }, + }); + expect(store.userIds()).toEqual([101, 102, 303]); + + patchState( + store, + updateEntities( + { + predicate: ({ id }) => id > 300, + changes: ({ id }) => ({ id: id - 300 }), + }, + { collection: 'user' } + ), + updateEntities( + { + predicate: ({ firstName }) => firstName === 'Jimmy1', + changes: { id: 1, firstName: 'Jimmy' }, + }, + { collection: 'user' } + ) + ); + + expect(store.userEntityMap()).toEqual({ + 1: { ...user1, id: 1, firstName: 'Jimmy' }, + 102: { ...user2, id: 102, firstName: 'Jimmy2' }, + 3: { ...user3, id: 3, lastName: 'Hendrix' }, + }); + expect(store.userIds()).toEqual([1, 102, 3]); + }); + + it('updates custom entity ids from specified collection', () => { + const Store = signalStore( + withEntities({ + entity: type(), + collection: 'todo', + }) + ); + const store = new Store(); + + patchState( + store, + addEntities([todo1, todo2, todo3], { + collection: 'todo', + selectId: (todo) => todo._id, + }), + updateEntities( + { + ids: [todo1._id, todo2._id], + changes: ({ _id }) => ({ _id: _id + 10, text: `Todo ${_id}` }), + }, + { collection: 'todo', selectId: (todo) => todo._id } + ), + updateEntities( + { + ids: [todo3._id], + changes: { _id: 'z30' }, + }, + { collection: 'todo', selectId: (todo) => todo._id } + ) + ); + + expect(store.todoEntityMap()).toEqual({ + x10: { ...todo1, _id: 'x10', text: 'Todo x' }, + y10: { ...todo2, _id: 'y10', text: 'Todo y' }, + z30: { ...todo3, _id: 'z30' }, + }); + expect(store.todoIds()).toEqual(['x10', 'y10', 'z30']); + + patchState( + store, + updateEntities( + { + predicate: ({ text }) => text.startsWith('Todo '), + changes: ({ _id }) => ({ _id: `${_id}0` }), + }, + { collection: 'todo', selectId: (todo) => todo._id } + ), + updateEntities( + { + predicate: ({ _id }) => _id === 'z30', + changes: { _id: 'z' }, + }, + { collection: 'todo', selectId: (todo) => todo._id } + ) + ); + + expect(store.todoEntityMap()).toEqual({ + x100: { ...todo1, _id: 'x100', text: 'Todo x' }, + y100: { ...todo2, _id: 'y100', text: 'Todo y' }, + z: { ...todo3, _id: 'z' }, + }); + expect(store.todoIds()).toEqual(['x100', 'y100', 'z']); + }); }); diff --git a/modules/signals/entities/spec/updaters/update-entity.spec.ts b/modules/signals/entities/spec/updaters/update-entity.spec.ts index fe5cd072af..e99cc9d780 100644 --- a/modules/signals/entities/spec/updaters/update-entity.spec.ts +++ b/modules/signals/entities/spec/updaters/update-entity.spec.ts @@ -76,14 +76,20 @@ describe('updateEntity', () => { patchState( store, - updateEntity({ - id: todo1._id, - changes: { text: '' }, - }), - updateEntity({ - id: 'a', - changes: ({ completed }) => ({ completed: !completed }), - }) + updateEntity( + { + id: todo1._id, + changes: { text: '' }, + }, + todoConfig + ), + updateEntity( + { + id: 'a', + changes: ({ completed }) => ({ completed: !completed }), + }, + todoConfig + ) ); expect(store.entityMap()).toBe(entityMap); @@ -112,14 +118,14 @@ describe('updateEntity', () => { }), updateEntity( { id: todo1._id, changes: { text: '' } }, - { collection: 'todo' } + { collection: 'todo', selectId: selectTodoId } ), updateEntity( { id: todo2._id, changes: ({ completed }) => ({ completed: !completed }), }, - { collection: 'todo' } + { collection: 'todo', selectId: (todo) => todo._id } ) ); @@ -172,4 +178,153 @@ describe('updateEntity', () => { expect(store.userIds()).toEqual([1, 2, 3]); expect(store.userEntities()).toEqual([user1, user2, user3]); }); + + it('updates an entity id', () => { + const Store = signalStore(withEntities()); + const store = new Store(); + + patchState( + store, + addEntities([user1, user2, user3]), + updateEntity({ + id: user1.id, + changes: ({ id }) => ({ id: id + 10 }), + }) + ); + + expect(store.entityMap()).toEqual({ + 11: { ...user1, id: 11 }, + 2: user2, + 3: user3, + }); + expect(store.ids()).toEqual([11, 2, 3]); + expect(store.entities()).toEqual([{ ...user1, id: 11 }, user2, user3]); + + patchState( + store, + updateEntity({ + id: 11, + changes: { id: 101, firstName: 'Jimmy1' }, + }), + updateEntity({ + id: user3.id, + changes: ({ id }) => ({ id: 303, firstName: `Stevie${id}` }), + }) + ); + + expect(store.entityMap()).toEqual({ + 101: { ...user1, id: 101, firstName: 'Jimmy1' }, + 2: user2, + 303: { ...user3, id: 303, firstName: 'Stevie3' }, + }); + expect(store.ids()).toEqual([101, 2, 303]); + }); + + it('updates a custom entity id', () => { + const Store = signalStore(withEntities()); + const store = new Store(); + + patchState( + store, + addEntities([todo1, todo2, todo3], { + selectId: (todo) => todo._id, + }), + updateEntity( + { + id: todo2._id, + changes: ({ _id, text }) => ({ _id: _id + 200, text: `${text} 200` }), + }, + { selectId: (todo) => todo._id } + ), + updateEntity( + { + id: todo3._id, + changes: { _id: 'z300', text: 'Todo 300' }, + }, + { selectId: (todo) => todo._id } + ) + ); + + expect(store.entityMap()).toEqual({ + x: todo1, + y200: { ...todo2, _id: 'y200', text: 'Buy eggs 200' }, + z300: { ...todo3, _id: 'z300', text: 'Todo 300' }, + }); + expect(store.ids()).toEqual(['x', 'y200', 'z300']); + }); + + it('updates an entity id from specified entity collection', () => { + const Store = signalStore( + withEntities({ + entity: type(), + collection: 'user', + }) + ); + const store = new Store(); + + patchState( + store, + addEntities([user1, user2, user3], { collection: 'user' }), + updateEntity( + { + id: user1.id, + changes: ({ id }) => ({ id: id + 100, firstName: 'Jimi' }), + }, + { collection: 'user' } + ), + updateEntity( + { + id: user2.id, + changes: { id: 202, lastName: 'Hendrix' }, + }, + { collection: 'user' } + ) + ); + + expect(store.userEntityMap()).toEqual({ + 101: { ...user1, id: 101, firstName: 'Jimi' }, + 202: { ...user2, id: 202, lastName: 'Hendrix' }, + 3: user3, + }); + expect(store.userIds()).toEqual([101, 202, 3]); + }); + + it('updates a custom entity id from specified entity collection', () => { + const Store = signalStore( + withEntities({ + entity: type(), + collection: 'todo', + }) + ); + const store = new Store(); + + patchState( + store, + addEntities([todo1, todo2, todo3], { + collection: 'todo', + selectId: (todo) => todo._id, + }), + updateEntity( + { + id: todo2._id, + changes: ({ _id }) => ({ _id: `${_id}200`, text: 'Todo 200' }), + }, + { collection: 'todo', selectId: (todo) => todo._id } + ), + updateEntity( + { + id: todo3._id, + changes: { _id: '303' }, + }, + { collection: 'todo', selectId: (todo) => todo._id } + ) + ); + + expect(store.todoEntityMap()).toEqual({ + x: todo1, + y200: { ...todo2, _id: 'y200', text: 'Todo 200' }, + 303: { ...todo3, _id: '303' }, + }); + expect(store.todoIds()).toEqual(['x', 'y200', '303']); + }); }); diff --git a/modules/signals/entities/src/helpers.ts b/modules/signals/entities/src/helpers.ts index 5fdb744300..fb5dbd2b8d 100644 --- a/modules/signals/entities/src/helpers.ts +++ b/modules/signals/entities/src/helpers.ts @@ -7,6 +7,7 @@ import { SelectEntityId, } from './models'; +declare const ngDevMode: unknown; const defaultSelectId: SelectEntityId<{ id: EntityId }> = (entity) => entity.id; export function getEntityIdSelector(config?: { @@ -166,11 +167,13 @@ export function removeEntitiesMutably( export function updateEntitiesMutably( state: EntityState, idsOrPredicate: EntityId[] | EntityPredicate, - changes: EntityChanges + changes: EntityChanges, + selectId: SelectEntityId ): DidMutate { const ids = Array.isArray(idsOrPredicate) ? idsOrPredicate : state.ids.filter((id) => idsOrPredicate(state.entityMap[id])); + let newIds: Record | undefined = undefined; let didMutate = DidMutate.None; for (const id of ids) { @@ -181,8 +184,32 @@ export function updateEntitiesMutably( typeof changes === 'function' ? changes(entity) : changes; state.entityMap[id] = { ...entity, ...changesRecord }; didMutate = DidMutate.Entities; + + const newId = selectId(state.entityMap[id]); + if (newId !== id) { + state.entityMap[newId] = state.entityMap[id]; + delete state.entityMap[id]; + + newIds = newIds || {}; + newIds[id] = newId; + } } } + if (newIds) { + state.ids = state.ids.map((id) => newIds[id] ?? id); + didMutate = DidMutate.Both; + } + + if (ngDevMode && state.ids.length !== Object.keys(state.entityMap).length) { + console.warn( + '@ngrx/signals/entities: Entities with IDs:', + ids, + 'are not updated correctly.', + 'Make sure to apply valid changes when using `updateEntity`,', + '`updateEntities`, and `updateAllEntities` updaters.' + ); + } + return didMutate; } diff --git a/modules/signals/entities/src/updaters/update-all-entities.ts b/modules/signals/entities/src/updaters/update-all-entities.ts index ba16833b4b..9618997b96 100644 --- a/modules/signals/entities/src/updaters/update-all-entities.ts +++ b/modules/signals/entities/src/updaters/update-all-entities.ts @@ -1,27 +1,55 @@ import { PartialStateUpdater } from '@ngrx/signals'; -import { EntityChanges, EntityState, NamedEntityState } from '../models'; +import { + EntityChanges, + EntityId, + EntityState, + NamedEntityState, + SelectEntityId, +} from '../models'; import { cloneEntityState, + getEntityIdSelector, getEntityStateKeys, getEntityUpdaterResult, updateEntitiesMutably, } from '../helpers'; -export function updateAllEntities( - changes: EntityChanges -): PartialStateUpdater>; export function updateAllEntities< Collection extends string, State extends NamedEntityState, Entity = State extends NamedEntityState ? E : never >( - changes: EntityChanges, + changes: EntityChanges>, + config: { + collection: Collection; + selectId: SelectEntityId>; + } +): PartialStateUpdater; +export function updateAllEntities< + Collection extends string, + State extends NamedEntityState, + Entity = State extends NamedEntityState< + infer E extends { id: EntityId }, + Collection + > + ? E + : never +>( + changes: EntityChanges>, config: { collection: Collection } ): PartialStateUpdater; +export function updateAllEntities( + changes: EntityChanges>, + config: { selectId: SelectEntityId> } +): PartialStateUpdater>; +export function updateAllEntities( + changes: EntityChanges> +): PartialStateUpdater>; export function updateAllEntities( changes: EntityChanges, - config?: { collection?: string } + config?: { collection?: string; selectId?: SelectEntityId } ): PartialStateUpdater | NamedEntityState> { + const selectId = getEntityIdSelector(config); const stateKeys = getEntityStateKeys(config); return (state) => { @@ -29,7 +57,8 @@ export function updateAllEntities( const didMutate = updateEntitiesMutably( clonedState, (state as Record)[stateKeys.idsKey], - changes + changes, + selectId ); return getEntityUpdaterResult(clonedState, stateKeys, didMutate); diff --git a/modules/signals/entities/src/updaters/update-entities.ts b/modules/signals/entities/src/updaters/update-entities.ts index a065c8931d..f6cc101634 100644 --- a/modules/signals/entities/src/updaters/update-entities.ts +++ b/modules/signals/entities/src/updaters/update-entities.ts @@ -5,22 +5,16 @@ import { EntityPredicate, EntityState, NamedEntityState, + SelectEntityId, } from '../models'; import { cloneEntityState, + getEntityIdSelector, getEntityStateKeys, getEntityUpdaterResult, updateEntitiesMutably, } from '../helpers'; -export function updateEntities(update: { - ids: EntityId[]; - changes: EntityChanges; -}): PartialStateUpdater>; -export function updateEntities(update: { - predicate: EntityPredicate; - changes: EntityChanges; -}): PartialStateUpdater>; export function updateEntities< Collection extends string, State extends NamedEntityState, @@ -28,9 +22,12 @@ export function updateEntities< >( update: { ids: EntityId[]; - changes: EntityChanges; + changes: EntityChanges>; }, - config: { collection: Collection } + config: { + collection: Collection; + selectId: SelectEntityId>; + } ): PartialStateUpdater; export function updateEntities< Collection extends string, @@ -39,16 +36,74 @@ export function updateEntities< >( update: { predicate: EntityPredicate; - changes: EntityChanges; + changes: EntityChanges>; + }, + config: { + collection: Collection; + selectId: SelectEntityId>; + } +): PartialStateUpdater; +export function updateEntities< + Collection extends string, + State extends NamedEntityState, + Entity = State extends NamedEntityState< + infer E extends { id: EntityId }, + Collection + > + ? E + : never +>( + update: { + ids: EntityId[]; + changes: EntityChanges>; + }, + config: { collection: Collection } +): PartialStateUpdater; +export function updateEntities< + Collection extends string, + State extends NamedEntityState, + Entity = State extends NamedEntityState< + infer E extends { id: EntityId }, + Collection + > + ? E + : never +>( + update: { + predicate: EntityPredicate; + changes: EntityChanges>; }, config: { collection: Collection } ): PartialStateUpdater; +export function updateEntities( + update: { + ids: EntityId[]; + changes: EntityChanges>; + }, + config: { selectId: SelectEntityId> } +): PartialStateUpdater>; +export function updateEntities( + update: { + predicate: EntityPredicate; + changes: EntityChanges>; + }, + config: { selectId: SelectEntityId> } +): PartialStateUpdater>; +export function updateEntities(update: { + ids: EntityId[]; + changes: EntityChanges>; +}): PartialStateUpdater>; +export function updateEntities(update: { + predicate: EntityPredicate; + changes: EntityChanges>; +}): PartialStateUpdater>; export function updateEntities( update: ({ ids: EntityId[] } | { predicate: EntityPredicate }) & { changes: EntityChanges; }, - config?: { collection?: string } + config?: { collection?: string; selectId?: SelectEntityId } ): PartialStateUpdater | NamedEntityState> { + const selectId = getEntityIdSelector(config); const stateKeys = getEntityStateKeys(config); const idsOrPredicate = 'ids' in update ? update.ids : update.predicate; @@ -57,7 +112,8 @@ export function updateEntities( const didMutate = updateEntitiesMutably( clonedState, idsOrPredicate, - update.changes + update.changes, + selectId ); return getEntityUpdaterResult(clonedState, stateKeys, didMutate); diff --git a/modules/signals/entities/src/updaters/update-entity.ts b/modules/signals/entities/src/updaters/update-entity.ts index f69a0e1456..72585f6c50 100644 --- a/modules/signals/entities/src/updaters/update-entity.ts +++ b/modules/signals/entities/src/updaters/update-entity.ts @@ -4,18 +4,16 @@ import { EntityId, EntityState, NamedEntityState, + SelectEntityId, } from '../models'; import { cloneEntityState, + getEntityIdSelector, getEntityStateKeys, getEntityUpdaterResult, updateEntitiesMutably, } from '../helpers'; -export function updateEntity(update: { - id: EntityId; - changes: EntityChanges; -}): PartialStateUpdater>; export function updateEntity< Collection extends string, State extends NamedEntityState, @@ -23,17 +21,48 @@ export function updateEntity< >( update: { id: EntityId; - changes: EntityChanges; + changes: EntityChanges>; + }, + config: { + collection: Collection; + selectId: SelectEntityId>; + } +): PartialStateUpdater; +export function updateEntity< + Collection extends string, + State extends NamedEntityState, + Entity = State extends NamedEntityState< + infer E extends { id: EntityId }, + Collection + > + ? E + : never +>( + update: { + id: EntityId; + changes: EntityChanges>; }, config: { collection: Collection } ): PartialStateUpdater; +export function updateEntity( + update: { + id: EntityId; + changes: EntityChanges>; + }, + config: { selectId: SelectEntityId> } +): PartialStateUpdater>; +export function updateEntity(update: { + id: EntityId; + changes: EntityChanges>; +}): PartialStateUpdater>; export function updateEntity( update: { id: EntityId; changes: EntityChanges; }, - config?: { collection?: string } + config?: { collection?: string; selectId?: SelectEntityId } ): PartialStateUpdater | NamedEntityState> { + const selectId = getEntityIdSelector(config); const stateKeys = getEntityStateKeys(config); return (state) => { @@ -41,7 +70,8 @@ export function updateEntity( const didMutate = updateEntitiesMutably( clonedState, [update.id], - update.changes + update.changes, + selectId ); return getEntityUpdaterResult(clonedState, stateKeys, didMutate); diff --git a/projects/ngrx.io/content/guide/signals/signal-store/entity-management.md b/projects/ngrx.io/content/guide/signals/signal-store/entity-management.md index bff975714c..3f604bf9c6 100644 --- a/projects/ngrx.io/content/guide/signals/signal-store/entity-management.md +++ b/projects/ngrx.io/content/guide/signals/signal-store/entity-management.md @@ -217,9 +217,9 @@ patchState(this.todoStore, removeEntities([2, 4])); The default property name for an identifier is `id` and is of type `string` or `number`. -It is possible to specify a custom ID selector, but the return type must still be a `string` or `number`. Custom ID selectors should be provided when adding or setting an entity. It is not possible to define it via `withEntities`. +It is possible to specify a custom ID selector, but the return type must still be a `string` or `number`. Custom ID selectors should be provided when adding, setting, or updating an entity. It is not possible to define it via `withEntities`. -Therefore, all variations of the `add*` and `set*` functions have an optional (last) parameter, which is an object literal that allows to specify the `selectId` function. +Therefore, all variations of the `add*`, `set*`, and `update*` functions have an optional (last) parameter, which is a config object that allows to specify the `selectId` function. For example: @@ -244,9 +244,11 @@ patchState( ); patchState(this.todoStore, setEntity({ key: 4, name: 'Dog Feeding', finished: false }, { selectId })); + +patchState(this.todoStore, updateAllEntities({ finished: true }, { selectId })); ``` -The `update*` and `remove*` methods, which expect an id value, automatically pick the right one. That is possible because every entity belongs to a map with its id as the key. +The `remove*` methods, which expect an id value, automatically pick the right one. That is possible because every entity belongs to a map with its id as the key. Theoretically, adding the same entity twice with different id names would be possible. For obvious reasons, we discourage you from doing that.