@@ -82,7 +82,8 @@ export type ReducerWithInitialState<S extends NotFunction<any>> = Reducer<S> & {
82
82
83
83
let hasWarnedAboutObjectNotation = false
84
84
85
- /**
85
+ export type CreateReducer = {
86
+ /**
86
87
* A utility function that allows defining a reducer as a mapping from action
87
88
* type to *case reducer* functions that handle these action types. The
88
89
* reducer's initial state is passed as the first argument.
@@ -146,12 +147,12 @@ const reducer = createReducer(
146
147
```
147
148
* @public
148
149
*/
149
- export function createReducer < S extends NotFunction < any > > (
150
- initialState : S | ( ( ) => S ) ,
151
- builderCallback : ( builder : ActionReducerMapBuilder < S > ) => void
152
- ) : ReducerWithInitialState < S >
150
+ < S extends NotFunction < any > > (
151
+ initialState : S | ( ( ) => S ) ,
152
+ builderCallback : ( builder : ActionReducerMapBuilder < S > ) => void
153
+ ) : ReducerWithInitialState < S >
153
154
154
- /**
155
+ /**
155
156
* A utility function that allows defining a reducer as a mapping from action
156
157
* type to *case reducer* functions that handle these action types. The
157
158
* reducer's initial state is passed as the first argument.
@@ -203,104 +204,118 @@ const counterReducer = createReducer(0, {
203
204
```
204
205
* @public
205
206
*/
206
- export function createReducer <
207
- S extends NotFunction < any > ,
208
- CR extends CaseReducers < S , any > = CaseReducers < S , any >
209
- > (
210
- initialState : S | ( ( ) => S ) ,
211
- actionsMap : CR ,
212
- actionMatchers ?: ActionMatcherDescriptionCollection < S > ,
213
- defaultCaseReducer ?: CaseReducer < S >
214
- ) : ReducerWithInitialState < S >
215
-
216
- export function createReducer < S extends NotFunction < any > > (
217
- initialState : S | ( ( ) => S ) ,
218
- mapOrBuilderCallback :
219
- | CaseReducers < S , any >
220
- | ( ( builder : ActionReducerMapBuilder < S > ) => void ) ,
221
- actionMatchers : ReadonlyActionMatcherDescriptionCollection < S > = [ ] ,
222
- defaultCaseReducer ?: CaseReducer < S >
223
- ) : ReducerWithInitialState < S > {
224
- if ( process . env . NODE_ENV !== 'production' ) {
225
- if ( typeof mapOrBuilderCallback === 'object' ) {
226
- if ( ! hasWarnedAboutObjectNotation ) {
227
- hasWarnedAboutObjectNotation = true
228
- console . warn (
229
- "The object notation for `createReducer` is deprecated, and will be removed in RTK 2.0. Please use the 'builder callback' notation instead: https://redux-toolkit.js.org/api/createReducer"
230
- )
207
+ <
208
+ S extends NotFunction < any > ,
209
+ CR extends CaseReducers < S , any > = CaseReducers < S , any >
210
+ > (
211
+ initialState : S | ( ( ) => S ) ,
212
+ actionsMap : CR ,
213
+ actionMatchers ?: ActionMatcherDescriptionCollection < S > ,
214
+ defaultCaseReducer ?: CaseReducer < S >
215
+ ) : ReducerWithInitialState < S >
216
+ }
217
+
218
+ export interface BuildCreateReducerConfiguration {
219
+ createNextState : < Base > (
220
+ base : Base ,
221
+ recipe : ( draft : Draft < Base > ) => void | Base | Draft < Base >
222
+ ) => Base
223
+ }
224
+
225
+ export function buildCreateReducer ( {
226
+ createNextState,
227
+ } : BuildCreateReducerConfiguration ) : CreateReducer {
228
+ return function createReducer < S extends NotFunction < any > > (
229
+ initialState : S | ( ( ) => S ) ,
230
+ mapOrBuilderCallback :
231
+ | CaseReducers < S , any >
232
+ | ( ( builder : ActionReducerMapBuilder < S > ) => void ) ,
233
+ actionMatchers : ReadonlyActionMatcherDescriptionCollection < S > = [ ] ,
234
+ defaultCaseReducer ?: CaseReducer < S >
235
+ ) : ReducerWithInitialState < S > {
236
+ if ( process . env . NODE_ENV !== 'production' ) {
237
+ if ( typeof mapOrBuilderCallback === 'object' ) {
238
+ if ( ! hasWarnedAboutObjectNotation ) {
239
+ hasWarnedAboutObjectNotation = true
240
+ console . warn (
241
+ "The object notation for `createReducer` is deprecated, and will be removed in RTK 2.0. Please use the 'builder callback' notation instead: https://redux-toolkit.js.org/api/createReducer"
242
+ )
243
+ }
231
244
}
232
245
}
233
- }
234
246
235
- let [ actionsMap , finalActionMatchers , finalDefaultCaseReducer ] =
236
- typeof mapOrBuilderCallback === 'function'
237
- ? executeReducerBuilderCallback ( mapOrBuilderCallback )
238
- : [ mapOrBuilderCallback , actionMatchers , defaultCaseReducer ]
239
-
240
- // Ensure the initial state gets frozen either way (if draftable)
241
- let getInitialState : ( ) => S
242
- if ( isStateFunction ( initialState ) ) {
243
- getInitialState = ( ) => freezeDraftable ( initialState ( ) )
244
- } else {
245
- const frozenInitialState = freezeDraftable ( initialState )
246
- getInitialState = ( ) => frozenInitialState
247
- }
247
+ let [ actionsMap , finalActionMatchers , finalDefaultCaseReducer ] =
248
+ typeof mapOrBuilderCallback === 'function'
249
+ ? executeReducerBuilderCallback ( mapOrBuilderCallback )
250
+ : [ mapOrBuilderCallback , actionMatchers , defaultCaseReducer ]
248
251
249
- function reducer ( state = getInitialState ( ) , action : any ) : S {
250
- let caseReducers = [
251
- actionsMap [ action . type ] ,
252
- ...finalActionMatchers
253
- . filter ( ( { matcher } ) => matcher ( action ) )
254
- . map ( ( { reducer } ) => reducer ) ,
255
- ]
256
- if ( caseReducers . filter ( ( cr ) => ! ! cr ) . length === 0 ) {
257
- caseReducers = [ finalDefaultCaseReducer ]
252
+ // Ensure the initial state gets frozen either way (if draftable)
253
+ let getInitialState : ( ) => S
254
+ if ( isStateFunction ( initialState ) ) {
255
+ getInitialState = ( ) => freezeDraftable ( initialState ( ) )
256
+ } else {
257
+ const frozenInitialState = freezeDraftable ( initialState )
258
+ getInitialState = ( ) => frozenInitialState
258
259
}
259
260
260
- return caseReducers . reduce ( ( previousState , caseReducer ) : S => {
261
- if ( caseReducer ) {
262
- if ( isDraft ( previousState ) ) {
263
- // If it's already a draft, we must already be inside a `createNextState` call,
264
- // likely because this is being wrapped in `createReducer`, `createSlice`, or nested
265
- // inside an existing draft. It's safe to just pass the draft to the mutator.
266
- const draft = previousState as Draft < S > // We can assume this is already a draft
267
- const result = caseReducer ( draft , action )
268
-
269
- if ( result === undefined ) {
270
- return previousState
271
- }
261
+ function reducer ( state = getInitialState ( ) , action : any ) : S {
262
+ let caseReducers = [
263
+ actionsMap [ action . type ] ,
264
+ ...finalActionMatchers
265
+ . filter ( ( { matcher } ) => matcher ( action ) )
266
+ . map ( ( { reducer } ) => reducer ) ,
267
+ ]
268
+ if ( caseReducers . filter ( ( cr ) => ! ! cr ) . length === 0 ) {
269
+ caseReducers = [ finalDefaultCaseReducer ]
270
+ }
272
271
273
- return result as S
274
- } else if ( ! isDraftable ( previousState ) ) {
275
- // If state is not draftable (ex: a primitive, such as 0), we want to directly
276
- // return the caseReducer func and not wrap it with produce.
277
- const result = caseReducer ( previousState as any , action )
272
+ return caseReducers . reduce ( ( previousState , caseReducer ) : S => {
273
+ if ( caseReducer ) {
274
+ if ( isDraft ( previousState ) ) {
275
+ // If it's already a draft, we must already be inside a `createNextState` call,
276
+ // likely because this is being wrapped in `createReducer`, `createSlice`, or nested
277
+ // inside an existing draft. It's safe to just pass the draft to the mutator.
278
+ const draft = previousState as Draft < S > // We can assume this is already a draft
279
+ const result = caseReducer ( draft , action )
278
280
279
- if ( result === undefined ) {
280
- if ( previousState === null ) {
281
+ if ( result === undefined ) {
281
282
return previousState
282
283
}
283
- throw Error (
284
- 'A case reducer on a non-draftable value must not return undefined'
285
- )
286
- }
287
284
288
- return result as S
289
- } else {
290
- // @ts -ignore createNextState() produces an Immutable<Draft<S>> rather
291
- // than an Immutable<S>, and TypeScript cannot find out how to reconcile
292
- // these two types.
293
- return createNextState ( previousState , ( draft : Draft < S > ) => {
294
- return caseReducer ( draft , action )
295
- } )
285
+ return result as S
286
+ } else if ( ! isDraftable ( previousState ) ) {
287
+ // If state is not draftable (ex: a primitive, such as 0), we want to directly
288
+ // return the caseReducer func and not wrap it with produce.
289
+ const result = caseReducer ( previousState as any , action )
290
+
291
+ if ( result === undefined ) {
292
+ if ( previousState === null ) {
293
+ return previousState
294
+ }
295
+ throw Error (
296
+ 'A case reducer on a non-draftable value must not return undefined'
297
+ )
298
+ }
299
+
300
+ return result as S
301
+ } else {
302
+ // @ts -ignore createNextState() produces an Immutable<Draft<S>> rather
303
+ // than an Immutable<S>, and TypeScript cannot find out how to reconcile
304
+ // these two types.
305
+ return createNextState ( previousState , ( draft : Draft < S > ) => {
306
+ return caseReducer ( draft , action )
307
+ } )
308
+ }
296
309
}
297
- }
298
310
299
- return previousState
300
- } , state )
301
- }
311
+ return previousState
312
+ } , state )
313
+ }
302
314
303
- reducer . getInitialState = getInitialState
315
+ reducer . getInitialState = getInitialState
304
316
305
- return reducer as ReducerWithInitialState < S >
317
+ return reducer as ReducerWithInitialState < S >
318
+ }
306
319
}
320
+
321
+ export const createReducer = buildCreateReducer ( { createNextState } )
0 commit comments