Skip to content

Commit 23f1114

Browse files
authored
Merge pull request #4183 from reduxjs/getinitialstate-entities
Add second parameter to getInitialState to prefill entities
2 parents 5ea4c31 + cb6717d commit 23f1114

File tree

4 files changed

+76
-39
lines changed

4 files changed

+76
-39
lines changed

packages/toolkit/src/entities/create_adapter.ts

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,17 @@
1-
import type {
2-
EntityDefinition,
3-
Comparer,
4-
IdSelector,
5-
EntityAdapter,
6-
EntityId,
7-
} from './models'
1+
import type { EntityAdapter, EntityId, EntityAdapterOptions } from './models'
82
import { createInitialStateFactory } from './entity_state'
93
import { createSelectorsFactory } from './state_selectors'
104
import { createSortedStateAdapter } from './sorted_state_adapter'
115
import { createUnsortedStateAdapter } from './unsorted_state_adapter'
6+
import type { WithRequiredProp } from '../tsHelpers'
127

13-
export interface EntityAdapterOptions<T, Id extends EntityId> {
14-
selectId?: IdSelector<T, Id>
15-
sortComparer?: false | Comparer<T>
16-
}
17-
18-
export function createEntityAdapter<T, Id extends EntityId>(options: {
19-
selectId: IdSelector<T, Id>
20-
sortComparer?: false | Comparer<T>
21-
}): EntityAdapter<T, Id>
8+
export function createEntityAdapter<T, Id extends EntityId>(
9+
options: WithRequiredProp<EntityAdapterOptions<T, Id>, 'selectId'>,
10+
): EntityAdapter<T, Id>
2211

23-
export function createEntityAdapter<T extends { id: EntityId }>(options?: {
24-
sortComparer?: false | Comparer<T>
25-
}): EntityAdapter<T, T['id']>
12+
export function createEntityAdapter<T extends { id: EntityId }>(
13+
options?: Omit<EntityAdapterOptions<T, T['id']>, 'selectId'>,
14+
): EntityAdapter<T, T['id']>
2615

2716
/**
2817
*
@@ -31,22 +20,22 @@ export function createEntityAdapter<T extends { id: EntityId }>(options?: {
3120
* @public
3221
*/
3322
export function createEntityAdapter<T>(
34-
options: {
35-
selectId?: IdSelector<T, EntityId>
36-
sortComparer?: false | Comparer<T>
37-
} = {},
23+
options: EntityAdapterOptions<T, EntityId> = {},
3824
): EntityAdapter<T, EntityId> {
39-
const { selectId, sortComparer }: EntityDefinition<T, EntityId> = {
25+
const {
26+
selectId,
27+
sortComparer,
28+
}: Required<EntityAdapterOptions<T, EntityId>> = {
4029
sortComparer: false,
4130
selectId: (instance: any) => instance.id,
4231
...options,
4332
}
4433

45-
const stateFactory = createInitialStateFactory<T, EntityId>()
46-
const selectorsFactory = createSelectorsFactory<T, EntityId>()
4734
const stateAdapter = sortComparer
4835
? createSortedStateAdapter(selectId, sortComparer)
4936
: createUnsortedStateAdapter(selectId)
37+
const stateFactory = createInitialStateFactory(stateAdapter)
38+
const selectorsFactory = createSelectorsFactory<T, EntityId>()
5039

5140
return {
5241
selectId,

packages/toolkit/src/entities/entity_state.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import type { EntityId, EntityState } from './models'
1+
import type {
2+
EntityId,
3+
EntityState,
4+
EntityStateAdapter,
5+
EntityStateFactory,
6+
} from './models'
27

38
export function getInitialEntityState<T, Id extends EntityId>(): EntityState<
49
T,
@@ -10,13 +15,23 @@ export function getInitialEntityState<T, Id extends EntityId>(): EntityState<
1015
}
1116
}
1217

13-
export function createInitialStateFactory<T, Id extends EntityId>() {
14-
function getInitialState(): EntityState<T, Id>
18+
export function createInitialStateFactory<T, Id extends EntityId>(
19+
stateAdapter: EntityStateAdapter<T, Id>,
20+
): EntityStateFactory<T, Id> {
21+
function getInitialState(
22+
state?: undefined,
23+
entities?: readonly T[] | Record<Id, T>,
24+
): EntityState<T, Id>
1525
function getInitialState<S extends object>(
1626
additionalState: S,
27+
entities?: readonly T[] | Record<Id, T>,
1728
): EntityState<T, Id> & S
18-
function getInitialState(additionalState: any = {}): any {
19-
return Object.assign(getInitialEntityState(), additionalState)
29+
function getInitialState(
30+
additionalState: any = {},
31+
entities?: readonly T[] | Record<Id, T>,
32+
): any {
33+
const state = Object.assign(getInitialEntityState(), additionalState)
34+
return entities ? stateAdapter.setAll(state, entities) : state
2035
}
2136

2237
return { getInitialState }

packages/toolkit/src/entities/models.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ export interface EntityState<T, Id extends EntityId> {
3535
/**
3636
* @public
3737
*/
38-
export interface EntityDefinition<T, Id extends EntityId> {
39-
selectId: IdSelector<T, Id>
40-
sortComparer: false | Comparer<T>
38+
export interface EntityAdapterOptions<T, Id extends EntityId> {
39+
selectId?: IdSelector<T, Id>
40+
sortComparer?: false | Comparer<T>
4141
}
4242

4343
export type PreventAny<S, T, Id extends EntityId> = CastAny<
@@ -166,15 +166,27 @@ export interface EntitySelectors<T, V, Id extends EntityId> {
166166
selectById: (state: V, id: Id) => Compute<UncheckedIndexedAccess<T>>
167167
}
168168

169+
/**
170+
* @public
171+
*/
172+
export interface EntityStateFactory<T, Id extends EntityId> {
173+
getInitialState(
174+
state?: undefined,
175+
entities?: Record<Id, T> | readonly T[],
176+
): EntityState<T, Id>
177+
getInitialState<S extends object>(
178+
state: S,
179+
entities?: Record<Id, T> | readonly T[],
180+
): EntityState<T, Id> & S
181+
}
182+
169183
/**
170184
* @public
171185
*/
172186
export interface EntityAdapter<T, Id extends EntityId>
173-
extends EntityStateAdapter<T, Id> {
174-
selectId: IdSelector<T, Id>
175-
sortComparer: false | Comparer<T>
176-
getInitialState(): EntityState<T, Id>
177-
getInitialState<S extends object>(state: S): EntityState<T, Id> & S
187+
extends EntityStateAdapter<T, Id>,
188+
EntityStateFactory<T, Id>,
189+
Required<EntityAdapterOptions<T, Id>> {
178190
getSelectors(
179191
selectState?: undefined,
180192
options?: GetSelectorsOptions,

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,27 @@ describe('Entity State', () => {
3535
})
3636
})
3737

38+
it('should let you provide initial entities', () => {
39+
const book1: BookModel = { id: 'a', title: 'First' }
40+
41+
const initialState = adapter.getInitialState(undefined, [book1])
42+
43+
expect(initialState).toEqual({
44+
ids: [book1.id],
45+
entities: { [book1.id]: book1 },
46+
})
47+
48+
const additionalProperties = { isHydrated: true }
49+
50+
const initialState2 = adapter.getInitialState(additionalProperties, [book1])
51+
52+
expect(initialState2).toEqual({
53+
...additionalProperties,
54+
ids: [book1.id],
55+
entities: { [book1.id]: book1 },
56+
})
57+
})
58+
3859
it('should allow methods to be passed as reducers', () => {
3960
const upsertBook = createAction<BookModel>('otherBooks/upsert')
4061

0 commit comments

Comments
 (0)