@@ -445,6 +445,8 @@ The same as [`addCase`](../api/createReducer#builderaddcase) for `createReducer`
445
445
``` ts no-transpile
446
446
const action = createAction (type )
447
447
context .addCase (action , reducer )
448
+ // or
449
+ context .addCase (type , reducer )
448
450
```
449
451
450
452
#### ` addMatcher `
@@ -465,6 +467,12 @@ const action = createAction(type)
465
467
context .exposeAction (action )
466
468
```
467
469
470
+ :::caution
471
+
472
+ ` exposeAction ` should only be called once (at maximum) within a ` handle ` callback - the same applies to ` exposeCaseReducer ` .
473
+
474
+ :::
475
+
468
476
#### ` exposeCaseReducer `
469
477
470
478
Attaches a value to ` slice.caseReducers[reducerName] ` .
@@ -478,12 +486,12 @@ context.exposeCaseReducer(reducer)
478
486
Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.
479
487
480
488
``` ts no-transpile
481
- const resetAction = createAction (type )
489
+ const resetAction = createAction (type + ' /reset ' )
482
490
const resetReducer = () => context .getInitialState ()
483
491
context
484
492
.addCase (resetAction , resetReducer )
485
- .exposeAction (resetAction )
486
- .exposeCaseReducer (resetReducer )
493
+ .exposeAction ({ reset: resetAction } )
494
+ .exposeCaseReducer ({ reset: resetReducer } )
487
495
```
488
496
489
497
#### ` selectSlice `
@@ -503,7 +511,7 @@ const aThunk =
503
511
504
512
The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [ RTK Query] ( /rtk-query/usage/customizing-create-api#creating-your-own-module ) .
505
513
506
- Creators are registered by using module augmentation to add a new key (their unique ` type ` ) to the ` SliceReducerCreators ` interface. The interface receives three type parameters ( ` State ` , ` CaseReducers ` and ` Name ` ), and each entry should use the ` ReducerCreatorEntry ` type utility.
514
+ Creators are registered by using module augmentation to add a new key (their unique ` type ` ) to the ` SliceReducerCreators ` interface. Each entry should use the ` ReducerCreatorEntry ` type utility.
507
515
508
516
``` ts no-transpile
509
517
const reducerCreatorType = Symbol (' reducerCreatorType' )
@@ -535,9 +543,9 @@ The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
535
543
536
544
The signature of the ` create ` method of the creator definition.
537
545
538
- :::caution ` CaseReducers ` and ` Name `
546
+ :::caution ` CaseReducers `
539
547
540
- Your ` Create ` type should not depend on the ` CaseReducers ` and ` Name ` type parameters , as these will not yet exist when the creator is being called.
548
+ Your ` Create ` type should not depend on the ` CaseReducers ` type parameter , as these will not yet exist when the creator is being called.
541
549
542
550
:::
543
551
@@ -580,6 +588,39 @@ const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
580
588
581
589
The second argument to the ` ReducerCreators ` type is a map from creator names to types, which you should supply if you're expecting to use any custom creators (anything other than ` reducer ` and ` preparedReducer ` ) within your own creator. For example, ` ReducerCreators<State, { asyncThunk: typeof asyncThunkCreator.type }> ` would allow you to call ` this.asyncThunk ` .
582
590
591
+ Alternatively, you can import the other creator's definition and use it directly.
592
+
593
+ ``` ts no-transpile
594
+ import { preparedReducerCreator } from ' @reduxjs/toolkit'
595
+
596
+ const batchedCreatorType = Symbol (' batchedCreatorType' )
597
+
598
+ declare module ' @reduxjs/toolkit' {
599
+ export interface SliceReducerCreators <
600
+ State ,
601
+ CaseReducers extends CreatorCaseReducers <State >,
602
+ Name extends string ,
603
+ ReducerPath extends string ,
604
+ > {
605
+ [batchedCreatorType ]: ReducerCreatorEntry <
606
+ <Payload >(
607
+ reducer : CaseReducer <State , PayloadAction <Payload >>,
608
+ ) => PreparedCaseReducerDefinition <
609
+ State ,
610
+ (payload : Payload ) => { payload: Payload ; meta: unknown }
611
+ >
612
+ >
613
+ }
614
+ }
615
+
616
+ const batchedCreator: ReducerCreator <typeof batchedCreatorType > = {
617
+ type: batchedCreatorType ,
618
+ create(reducer ) {
619
+ return preparedReducerCreator .create (prepareAutoBatched (), reducer )
620
+ },
621
+ }
622
+ ```
623
+
583
624
:::
584
625
585
626
:::note Ensuring compatible state
@@ -653,6 +694,8 @@ In order to ensure that the definitions are correctly filtered to only include t
653
694
}
654
695
```
655
696
697
+ To relate back to the context methods, it should describe what you will pass to ` context.exposeAction ` from a handler.
698
+
656
699
For example, with (a simplified version of) the ` asyncThunk ` creator:
657
700
658
701
``` ts no-transpile
@@ -691,6 +734,8 @@ declare module '@reduxjs/toolkit' {
691
734
692
735
Similar to ` actions ` , except for ` slice.caseReducers ` .
693
736
737
+ It describes what you will pass to ` context.exposeCaseReducer ` from a handler.
738
+
694
739
For example, with the ` preparedReducer ` creator:
695
740
696
741
``` ts no-transpile
@@ -956,6 +1001,8 @@ const paginationCreator: ReducerCreator<typeof paginationCreatorType> = {
956
1001
type: paginationCreatorType ,
957
1002
create() {
958
1003
return {
1004
+ // calling `this.reducer` assumes we'll be calling the creator as `create.paginationMethods()`
1005
+ // if we don't want this assumption, we could use `reducerCreator.create` instead
959
1006
prevPage: this .reducer ((state : PaginationState ) => {
960
1007
state .page --
961
1008
}),
@@ -1011,7 +1058,7 @@ declare module '@reduxjs/toolkit' {
1011
1058
ReducerPath extends string ,
1012
1059
> {
1013
1060
[historyCreatorType ]: ReducerCreatorEntry <
1014
- // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
1061
+ // make sure the creator is only called when state is compatible
1015
1062
State extends HistoryState <any >
1016
1063
? (this : ReducerCreators <State >) => {
1017
1064
undo: CaseReducerDefinition <State , PayloadAction >
@@ -1063,18 +1110,22 @@ const historyCreator: ReducerCreator<typeof historyCreatorType> = {
1063
1110
state .past .push (historyEntry )
1064
1111
}
1065
1112
}),
1113
+ // highlight-start
1114
+ // here we're creating a reducer definition that our `handle` method will be called with
1066
1115
reset: {
1067
1116
_reducerDefinitionType: historyCreatorType ,
1068
1117
type: ' reset' ,
1069
1118
},
1119
+ // highlight-end
1070
1120
}
1071
1121
},
1072
1122
handle(details , definition , context ) {
1073
1123
if (definition .type !== ' reset' ) {
1074
1124
throw new Error (' Unrecognised definition type: ' + definition .type )
1075
1125
}
1076
- // use the normal reducer creator to create a case reducer and action creator
1077
1126
const resetReducer = () => context .getInitialState ()
1127
+ // you can call other creators' `handle` methods if needed
1128
+ // here we're reusing `reducerCreator` to get the expected behaviour of making an action creator for our reducer
1078
1129
reducerCreator .handle (details , reducerCreator .create (resetReducer ), context )
1079
1130
},
1080
1131
}
@@ -1151,7 +1202,7 @@ const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
1151
1202
reducer : CaseReducer <any , A >,
1152
1203
): CaseReducer <HistoryState <any >, A > {
1153
1204
return (state , action ) => {
1154
- const [nextState, redoPatch, undoPatch ] = produceWithPatches (
1205
+ const [nextState, redoPatches, undoPatches ] = produceWithPatches (
1155
1206
state ,
1156
1207
(draft ) => {
1157
1208
const result = reducer (draft .present , action )
@@ -1165,8 +1216,8 @@ const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
1165
1216
if (undoable ) {
1166
1217
finalState = createNextState (finalState , (draft ) => {
1167
1218
draft .past .push ({
1168
- undo: undoPatch ,
1169
- redo: redoPatch ,
1219
+ undo: undoPatches ,
1220
+ redo: redoPatches ,
1170
1221
})
1171
1222
draft .future = []
1172
1223
})
@@ -1221,3 +1272,55 @@ const postSliceWithHistory = createAppSlice({
1221
1272
const { undo, redo, reset, updateTitle, togglePinned } =
1222
1273
postSliceWithHistory .actions
1223
1274
```
1275
+
1276
+ :::tip ` history-adapter `
1277
+
1278
+ This example is a somewhat simplified version of the [ ` history-adapter ` ] ( https://www.npmjs.com/package/history-adapter ) package, which provides a ` createHistoryAdapter ` utility that can be used to add undo/redo functionality to a slice.
1279
+
1280
+ ``` ts no-transpile
1281
+ import {
1282
+ createHistoryAdapter ,
1283
+ historyMethodsCreator ,
1284
+ undoableCreatorsCreator ,
1285
+ } from ' history-adapter/redux'
1286
+
1287
+ const createAppSlice = buildCreateSlice ({
1288
+ creators: {
1289
+ historyMethods: historyMethodsCreator ,
1290
+ undoableCreators: undoableCreatorsCreator ,
1291
+ },
1292
+ })
1293
+
1294
+ const postHistoryAdapter = createHistoryAdapter <Post >({ limit: 5 })
1295
+
1296
+ const postSliceWithHistory = createAppSlice ({
1297
+ name: ' post' ,
1298
+ initialState: postHistoryAdapter .getInitialState ({
1299
+ title: ' ' ,
1300
+ pinned: false ,
1301
+ }),
1302
+ reducers : (create ) => {
1303
+ const createUndoable = create .undoableCreators (postHistoryAdapter )
1304
+ return {
1305
+ ... create .historyMethods (postHistoryAdapter ),
1306
+ updateTitle: createUndoable .preparedReducer (
1307
+ postHistoryAdapter .withPayload <string >(),
1308
+ (state , action ) => {
1309
+ state .title = action .payload
1310
+ },
1311
+ ),
1312
+ togglePinned: createUndoable .preparedReducer (
1313
+ postHistoryAdapter .withoutPayload (),
1314
+ (state , action ) => {
1315
+ state .pinned = ! state .pinned
1316
+ },
1317
+ ),
1318
+ }
1319
+ },
1320
+ })
1321
+
1322
+ const { undo, redo, reset, updateTitle, togglePinned } =
1323
+ postSliceWithHistory .actions
1324
+ ```
1325
+
1326
+ :::
0 commit comments