@@ -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,90 +147,104 @@ 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 >
153
-
154
- export function createReducer < S extends NotFunction < any > > (
155
- initialState : S | ( ( ) => S ) ,
156
- mapOrBuilderCallback : ( builder : ActionReducerMapBuilder < S > ) => void
157
- ) : ReducerWithInitialState < S > {
158
- if ( process . env . NODE_ENV !== 'production' ) {
159
- if ( typeof mapOrBuilderCallback === 'object' ) {
160
- throw new Error (
161
- "The object notation for `createReducer` has been removed. Please use the 'builder callback' notation instead: https://redux-toolkit.js.org/api/createReducer"
162
- )
163
- }
164
- }
150
+ < S extends NotFunction < any > > (
151
+ initialState : S | ( ( ) => S ) ,
152
+ builderCallback : ( builder : ActionReducerMapBuilder < S > ) => void
153
+ ) : ReducerWithInitialState < S >
154
+ }
155
+
156
+ export interface BuildCreateReducerConfiguration {
157
+ createNextState : < Base > (
158
+ base : Base ,
159
+ recipe : ( draft : Draft < Base > ) => void | Base | Draft < Base >
160
+ ) => Base
161
+ }
165
162
166
- let [ actionsMap , finalActionMatchers , finalDefaultCaseReducer ] =
167
- executeReducerBuilderCallback ( mapOrBuilderCallback )
163
+ export function buildCreateReducer ( {
164
+ createNextState,
165
+ } : BuildCreateReducerConfiguration ) : CreateReducer {
166
+ return function createReducer < S extends NotFunction < any > > (
167
+ initialState : S | ( ( ) => S ) ,
168
+ mapOrBuilderCallback : ( builder : ActionReducerMapBuilder < S > ) => void
169
+ ) : ReducerWithInitialState < S > {
170
+ if ( process . env . NODE_ENV !== 'production' ) {
171
+ if ( typeof mapOrBuilderCallback === 'object' ) {
172
+ throw new Error (
173
+ "The object notation for `createReducer` has been removed. Please use the 'builder callback' notation instead: https://redux-toolkit.js.org/api/createReducer"
174
+ )
175
+ }
176
+ }
168
177
169
- // Ensure the initial state gets frozen either way (if draftable)
170
- let getInitialState : ( ) => S
171
- if ( isStateFunction ( initialState ) ) {
172
- getInitialState = ( ) => freezeDraftable ( initialState ( ) )
173
- } else {
174
- const frozenInitialState = freezeDraftable ( initialState )
175
- getInitialState = ( ) => frozenInitialState
176
- }
178
+ let [ actionsMap , finalActionMatchers , finalDefaultCaseReducer ] =
179
+ executeReducerBuilderCallback ( mapOrBuilderCallback )
177
180
178
- function reducer ( state = getInitialState ( ) , action : any ) : S {
179
- let caseReducers = [
180
- actionsMap [ action . type ] ,
181
- ...finalActionMatchers
182
- . filter ( ( { matcher } ) => matcher ( action ) )
183
- . map ( ( { reducer } ) => reducer ) ,
184
- ]
185
- if ( caseReducers . filter ( ( cr ) => ! ! cr ) . length === 0 ) {
186
- caseReducers = [ finalDefaultCaseReducer ]
181
+ // Ensure the initial state gets frozen either way (if draftable)
182
+ let getInitialState : ( ) => S
183
+ if ( isStateFunction ( initialState ) ) {
184
+ getInitialState = ( ) => freezeDraftable ( initialState ( ) )
185
+ } else {
186
+ const frozenInitialState = freezeDraftable ( initialState )
187
+ getInitialState = ( ) => frozenInitialState
187
188
}
188
189
189
- return caseReducers . reduce ( ( previousState , caseReducer ) : S => {
190
- if ( caseReducer ) {
191
- if ( isDraft ( previousState ) ) {
192
- // If it's already a draft, we must already be inside a `createNextState` call,
193
- // likely because this is being wrapped in `createReducer`, `createSlice`, or nested
194
- // inside an existing draft. It's safe to just pass the draft to the mutator.
195
- const draft = previousState as Draft < S > // We can assume this is already a draft
196
- const result = caseReducer ( draft , action )
197
-
198
- if ( result === undefined ) {
199
- return previousState
200
- }
190
+ function reducer ( state = getInitialState ( ) , action : any ) : S {
191
+ let caseReducers = [
192
+ actionsMap [ action . type ] ,
193
+ ...finalActionMatchers
194
+ . filter ( ( { matcher } ) => matcher ( action ) )
195
+ . map ( ( { reducer } ) => reducer ) ,
196
+ ]
197
+ if ( caseReducers . filter ( ( cr ) => ! ! cr ) . length === 0 ) {
198
+ caseReducers = [ finalDefaultCaseReducer ]
199
+ }
201
200
202
- return result as S
203
- } else if ( ! isDraftable ( previousState ) ) {
204
- // If state is not draftable (ex: a primitive, such as 0), we want to directly
205
- // return the caseReducer func and not wrap it with produce.
206
- const result = caseReducer ( previousState as any , action )
201
+ return caseReducers . reduce ( ( previousState , caseReducer ) : S => {
202
+ if ( caseReducer ) {
203
+ if ( isDraft ( previousState ) ) {
204
+ // If it's already a draft, we must already be inside a `createNextState` call,
205
+ // likely because this is being wrapped in `createReducer`, `createSlice`, or nested
206
+ // inside an existing draft. It's safe to just pass the draft to the mutator.
207
+ const draft = previousState as Draft < S > // We can assume this is already a draft
208
+ const result = caseReducer ( draft , action )
207
209
208
- if ( result === undefined ) {
209
- if ( previousState === null ) {
210
+ if ( result === undefined ) {
210
211
return previousState
211
212
}
212
- throw Error (
213
- 'A case reducer on a non-draftable value must not return undefined'
214
- )
215
- }
216
213
217
- return result as S
218
- } else {
219
- // @ts -ignore createNextState() produces an Immutable<Draft<S>> rather
220
- // than an Immutable<S>, and TypeScript cannot find out how to reconcile
221
- // these two types.
222
- return createNextState ( previousState , ( draft : Draft < S > ) => {
223
- return caseReducer ( draft , action )
224
- } )
214
+ return result as S
215
+ } else if ( ! isDraftable ( previousState ) ) {
216
+ // If state is not draftable (ex: a primitive, such as 0), we want to directly
217
+ // return the caseReducer func and not wrap it with produce.
218
+ const result = caseReducer ( previousState as any , action )
219
+
220
+ if ( result === undefined ) {
221
+ if ( previousState === null ) {
222
+ return previousState
223
+ }
224
+ throw Error (
225
+ 'A case reducer on a non-draftable value must not return undefined'
226
+ )
227
+ }
228
+
229
+ return result as S
230
+ } else {
231
+ // @ts -ignore createNextState() produces an Immutable<Draft<S>> rather
232
+ // than an Immutable<S>, and TypeScript cannot find out how to reconcile
233
+ // these two types.
234
+ return createNextState ( previousState , ( draft : Draft < S > ) => {
235
+ return caseReducer ( draft , action )
236
+ } )
237
+ }
225
238
}
226
- }
227
239
228
- return previousState
229
- } , state )
230
- }
240
+ return previousState
241
+ } , state )
242
+ }
231
243
232
- reducer . getInitialState = getInitialState
244
+ reducer . getInitialState = getInitialState
233
245
234
- return reducer as ReducerWithInitialState < S >
246
+ return reducer as ReducerWithInitialState < S >
247
+ }
235
248
}
249
+
250
+ export const createReducer = buildCreateReducer ( { createNextState } )
0 commit comments