diff --git a/packages/toolkit/src/entities/create_adapter.ts b/packages/toolkit/src/entities/create_adapter.ts index 726ad82c32..de9ec83f19 100644 --- a/packages/toolkit/src/entities/create_adapter.ts +++ b/packages/toolkit/src/entities/create_adapter.ts @@ -1,28 +1,17 @@ -import type { - EntityDefinition, - Comparer, - IdSelector, - EntityAdapter, - EntityId, -} from './models' +import type { EntityAdapter, EntityId, EntityAdapterOptions } from './models' import { createInitialStateFactory } from './entity_state' import { createSelectorsFactory } from './state_selectors' import { createSortedStateAdapter } from './sorted_state_adapter' import { createUnsortedStateAdapter } from './unsorted_state_adapter' +import type { WithRequiredProp } from '../tsHelpers' -export interface EntityAdapterOptions { - selectId?: IdSelector - sortComparer?: false | Comparer -} - -export function createEntityAdapter(options: { - selectId: IdSelector - sortComparer?: false | Comparer -}): EntityAdapter +export function createEntityAdapter( + options: WithRequiredProp, 'selectId'>, +): EntityAdapter -export function createEntityAdapter(options?: { - sortComparer?: false | Comparer -}): EntityAdapter +export function createEntityAdapter( + options?: Omit, 'selectId'>, +): EntityAdapter /** * @@ -31,22 +20,22 @@ export function createEntityAdapter(options?: { * @public */ export function createEntityAdapter( - options: { - selectId?: IdSelector - sortComparer?: false | Comparer - } = {}, + options: EntityAdapterOptions = {}, ): EntityAdapter { - const { selectId, sortComparer }: EntityDefinition = { + const { + selectId, + sortComparer, + }: Required> = { sortComparer: false, selectId: (instance: any) => instance.id, ...options, } - const stateFactory = createInitialStateFactory() - const selectorsFactory = createSelectorsFactory() const stateAdapter = sortComparer ? createSortedStateAdapter(selectId, sortComparer) : createUnsortedStateAdapter(selectId) + const stateFactory = createInitialStateFactory(stateAdapter) + const selectorsFactory = createSelectorsFactory() return { selectId, diff --git a/packages/toolkit/src/entities/entity_state.ts b/packages/toolkit/src/entities/entity_state.ts index 6b0744cdee..9631aec6b5 100644 --- a/packages/toolkit/src/entities/entity_state.ts +++ b/packages/toolkit/src/entities/entity_state.ts @@ -1,4 +1,9 @@ -import type { EntityId, EntityState } from './models' +import type { + EntityId, + EntityState, + EntityStateAdapter, + EntityStateFactory, +} from './models' export function getInitialEntityState(): EntityState< T, @@ -10,13 +15,23 @@ export function getInitialEntityState(): EntityState< } } -export function createInitialStateFactory() { - function getInitialState(): EntityState +export function createInitialStateFactory( + stateAdapter: EntityStateAdapter, +): EntityStateFactory { + function getInitialState( + state?: undefined, + entities?: readonly T[] | Record, + ): EntityState function getInitialState( additionalState: S, + entities?: readonly T[] | Record, ): EntityState & S - function getInitialState(additionalState: any = {}): any { - return Object.assign(getInitialEntityState(), additionalState) + function getInitialState( + additionalState: any = {}, + entities?: readonly T[] | Record, + ): any { + const state = Object.assign(getInitialEntityState(), additionalState) + return entities ? stateAdapter.setAll(state, entities) : state } return { getInitialState } diff --git a/packages/toolkit/src/entities/models.ts b/packages/toolkit/src/entities/models.ts index a7ec0238d9..76de9872fd 100644 --- a/packages/toolkit/src/entities/models.ts +++ b/packages/toolkit/src/entities/models.ts @@ -35,9 +35,9 @@ export interface EntityState { /** * @public */ -export interface EntityDefinition { - selectId: IdSelector - sortComparer: false | Comparer +export interface EntityAdapterOptions { + selectId?: IdSelector + sortComparer?: false | Comparer } export type PreventAny = CastAny< @@ -166,15 +166,27 @@ export interface EntitySelectors { selectById: (state: V, id: Id) => Compute> } +/** + * @public + */ +export interface EntityStateFactory { + getInitialState( + state?: undefined, + entities?: Record | readonly T[], + ): EntityState + getInitialState( + state: S, + entities?: Record | readonly T[], + ): EntityState & S +} + /** * @public */ export interface EntityAdapter - extends EntityStateAdapter { - selectId: IdSelector - sortComparer: false | Comparer - getInitialState(): EntityState - getInitialState(state: S): EntityState & S + extends EntityStateAdapter, + EntityStateFactory, + Required> { getSelectors( selectState?: undefined, options?: GetSelectorsOptions, diff --git a/packages/toolkit/src/entities/tests/entity_state.test.ts b/packages/toolkit/src/entities/tests/entity_state.test.ts index 65accf475c..999ee502b9 100644 --- a/packages/toolkit/src/entities/tests/entity_state.test.ts +++ b/packages/toolkit/src/entities/tests/entity_state.test.ts @@ -35,6 +35,27 @@ describe('Entity State', () => { }) }) + it('should let you provide initial entities', () => { + const book1: BookModel = { id: 'a', title: 'First' } + + const initialState = adapter.getInitialState(undefined, [book1]) + + expect(initialState).toEqual({ + ids: [book1.id], + entities: { [book1.id]: book1 }, + }) + + const additionalProperties = { isHydrated: true } + + const initialState2 = adapter.getInitialState(additionalProperties, [book1]) + + expect(initialState2).toEqual({ + ...additionalProperties, + ids: [book1.id], + entities: { [book1.id]: book1 }, + }) + }) + it('should allow methods to be passed as reducers', () => { const upsertBook = createAction('otherBooks/upsert')