Skip to content

Commit 0c2afc9

Browse files
committed
Updated readme with new section about createReducer API
1 parent 01bc1df commit 0c2afc9

File tree

7 files changed

+134
-30
lines changed

7 files changed

+134
-30
lines changed

README.md

+64-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ _"This guide is a **living compendium** documenting the most important patterns
88

99
:star: _Found it useful? Want more updates?_ [**Show your support by giving a :star:**](https://github.com/piotrwitek/react-redux-typescript-guide/stargazers)
1010

11-
:tada: _Now updated to support **TypeScript v3.1**_ :tada:
11+
:tada: _Now updated to support **TypeScript v3.4**_ :tada:
1212

1313
<a href="https://www.buymeacoffee.com/zh9guxbA5">
1414
<img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me a Coffee">
@@ -73,7 +73,8 @@ Issues can be funded by anyone interested in them being resolved. Reward will be
7373
- [Action Creators](#action-creators)
7474
- [Reducers](#reducers)
7575
- [State with Type-level Immutability](#state-with-type-level-immutability)
76-
- [Typing reducer](#typing-reducer)
76+
- [Typing regular reducer](#typing-regular-reducer)
77+
- [Typing reducer with `typesafe-actions`](#typing-reducer-with-typesafe-actions)
7778
- [Testing reducer](#testing-reducer)
7879
- [Async Flow with `redux-observable`](#async-flow-with-redux-observable)
7980
- [Typing Epics](#typing-epics)
@@ -1041,13 +1042,18 @@ Can be imported in connected components to provide type-safety to Redux `connect
10411042
Can be imported in various layers receiving or sending redux actions like: reducers, sagas or redux-observables epics
10421043
10431044
```tsx
1045+
import { StateType, ActionType } from 'typesafe-actions';
1046+
10441047
declare module 'MyTypes' {
1045-
import { StateType, ActionType } from 'typesafe-actions';
10461048
export type Store = StateType<typeof import('./index').default>;
10471049
export type RootAction = ActionType<typeof import('./root-action').default>;
10481050
export type RootState = StateType<typeof import('./root-reducer').default>;
10491051
}
10501052

1053+
declare module 'typesafe-actions' {
1054+
export type RootAction = ActionType<typeof import('./root-action').default>;
1055+
}
1056+
10511057
```
10521058
10531059
[⇧ back to top](#table-of-contents)
@@ -1101,7 +1107,7 @@ export default store;
11011107
> We'll be using a battle-tested library [![NPM Downloads](https://img.shields.io/npm/dm/typesafe-actions.svg)](https://www.npmjs.com/package/typesafe-actions)
11021108
that'll help retain complete type soundness and simplify maintenace of **types in Redux Architectures** [`typesafe-actions`](https://github.com/piotrwitek/typesafe-actions#typesafe-actions)
11031109
1104-
> You can find more real-world examples and in-depth tutorial in: [Typesafe-Actions - The Mighty Tutorial](https://github.com/piotrwitek/typesafe-actions#behold-the-mighty-tutorial)!
1110+
> You can find more real-world examples and in-depth tutorial in: [Typesafe-Actions - Tutorial](https://github.com/piotrwitek/typesafe-actions#tutorial)!
11051111
11061112
A solution below is using a simple factory function to automate the creation of type-safe action creators. The goal is to decrease maintenance effort and reduce code repetition of type annotations for actions and creators. The result is completely typesafe action-creators and their actions.
11071113
@@ -1229,15 +1235,19 @@ import { Todo, TodosFilter } from './models';
12291235
import * as actions from './actions';
12301236
import { ADD, CHANGE_FILTER, TOGGLE } from './constants';
12311237

1232-
export type TodosState = {
1233-
readonly todos: Todo[];
1234-
readonly todosFilter: TodosFilter;
1235-
};
1236-
12371238
export type TodosAction = ActionType<typeof actions>;
12381239

1240+
export type TodosState = Readonly<{
1241+
todos: Todo[];
1242+
todosFilter: TodosFilter;
1243+
}>;
1244+
const initialState: TodosState = {
1245+
todos: [],
1246+
todosFilter: TodosFilter.All,
1247+
};
1248+
12391249
export default combineReducers<TodosState, TodosAction>({
1240-
todos: (state = [], action) => {
1250+
todos: (state = initialState.todos, action) => {
12411251
switch (action.type) {
12421252
case ADD:
12431253
return [...state, action.payload];
@@ -1253,7 +1263,7 @@ export default combineReducers<TodosState, TodosAction>({
12531263
return state;
12541264
}
12551265
},
1256-
todosFilter: (state = TodosFilter.All, action) => {
1266+
todosFilter: (state = initialState.todosFilter, action) => {
12571267
switch (action.type) {
12581268
case CHANGE_FILTER:
12591269
return action.payload;
@@ -1268,6 +1278,49 @@ export default combineReducers<TodosState, TodosAction>({
12681278
12691279
[⇧ back to top](#table-of-contents)
12701280
1281+
### Typing reducer with `typesafe-actions`
1282+
> Notice we are not required to use any generic type parameter in the API. Try to compare it with regular reducer as they are equivalent.
1283+
1284+
```tsx
1285+
import { combineReducers } from 'redux';
1286+
import { createReducer } from 'typesafe-actions';
1287+
1288+
import { Todo, TodosFilter } from './models';
1289+
import { ADD, CHANGE_FILTER, TOGGLE } from './constants';
1290+
1291+
export type TodosState = Readonly<{
1292+
todos: Todo[];
1293+
todosFilter: TodosFilter;
1294+
}>;
1295+
const initialState: TodosState = {
1296+
todos: [],
1297+
todosFilter: TodosFilter.All,
1298+
};
1299+
1300+
const todos = createReducer(initialState.todos)
1301+
.handleAction(ADD, (state, action) => [...state, action.payload])
1302+
.handleAction(TOGGLE, (state, action) =>
1303+
state.map(item =>
1304+
item.id === action.payload
1305+
? { ...item, completed: !item.completed }
1306+
: item
1307+
)
1308+
);
1309+
1310+
const todosFilter = createReducer(initialState.todosFilter).handleAction(
1311+
CHANGE_FILTER,
1312+
(state, action) => action.payload
1313+
);
1314+
1315+
export default combineReducers({
1316+
todos,
1317+
todosFilter,
1318+
});
1319+
1320+
```
1321+
1322+
[⇧ back to top](#table-of-contents)
1323+
12711324
### Testing reducer
12721325
12731326
```tsx

README_SOURCE.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ _"This guide is a **living compendium** documenting the most important patterns
88

99
:star: _Found it useful? Want more updates?_ [**Show your support by giving a :star:**](https://github.com/piotrwitek/react-redux-typescript-guide/stargazers)
1010

11-
:tada: _Now updated to support **TypeScript v3.1**_ :tada:
11+
:tada: _Now updated to support **TypeScript v3.4**_ :tada:
1212

1313
<a href="https://www.buymeacoffee.com/zh9guxbA5">
1414
<img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me a Coffee">
@@ -73,7 +73,8 @@ Issues can be funded by anyone interested in them being resolved. Reward will be
7373
- [Action Creators](#action-creators)
7474
- [Reducers](#reducers)
7575
- [State with Type-level Immutability](#state-with-type-level-immutability)
76-
- [Typing reducer](#typing-reducer)
76+
- [Typing regular reducer](#typing-regular-reducer)
77+
- [Typing reducer with `typesafe-actions`](#typing-reducer-with-typesafe-actions)
7778
- [Testing reducer](#testing-reducer)
7879
- [Async Flow with `redux-observable`](#async-flow-with-redux-observable)
7980
- [Typing Epics](#typing-epics)
@@ -396,7 +397,7 @@ When creating a store instance we don't need to provide any additional types. It
396397
> We'll be using a battle-tested library [![NPM Downloads](https://img.shields.io/npm/dm/typesafe-actions.svg)](https://www.npmjs.com/package/typesafe-actions)
397398
that'll help retain complete type soundness and simplify maintenace of **types in Redux Architectures** [`typesafe-actions`](https://github.com/piotrwitek/typesafe-actions#typesafe-actions)
398399
399-
> You can find more real-world examples and in-depth tutorial in: [Typesafe-Actions - The Mighty Tutorial](https://github.com/piotrwitek/typesafe-actions#behold-the-mighty-tutorial)!
400+
> You can find more real-world examples and in-depth tutorial in: [Typesafe-Actions - Tutorial](https://github.com/piotrwitek/typesafe-actions#tutorial)!
400401
401402
A solution below is using a simple factory function to automate the creation of type-safe action creators. The goal is to decrease maintenance effort and reduce code repetition of type annotations for actions and creators. The result is completely typesafe action-creators and their actions.
402403
@@ -481,6 +482,13 @@ state.containerObject.numbers.push(1); // TS Error: cannot use mutator methods
481482
482483
[⇧ back to top](#table-of-contents)
483484
485+
### Typing reducer with `typesafe-actions`
486+
> Notice we are not required to use any generic type parameter in the API. Try to compare it with regular reducer as they are equivalent.
487+
488+
::codeblock='playground/src/features/todos/reducer-ta.ts'::
489+
490+
[⇧ back to top](#table-of-contents)
491+
484492
### Testing reducer
485493
486494
::codeblock='playground/src/features/todos/reducer.spec.ts'::

playground/package-lock.json

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

playground/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
"rxjs": "6.4.0",
4747
"tslib": "1.9.3",
4848
"tslint": "5.15.0",
49-
"typesafe-actions": "4.0.0",
50-
"typescript": "3.1.6",
49+
"typesafe-actions": "4.1.0",
50+
"typescript": "3.4.4",
5151
"utility-types": "3.5.0"
5252
},
5353
"browserslist": [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { combineReducers } from 'redux';
2+
import { createReducer } from 'typesafe-actions';
3+
4+
import { Todo, TodosFilter } from './models';
5+
import { ADD, CHANGE_FILTER, TOGGLE } from './constants';
6+
7+
export type TodosState = Readonly<{
8+
todos: Todo[];
9+
todosFilter: TodosFilter;
10+
}>;
11+
const initialState: TodosState = {
12+
todos: [],
13+
todosFilter: TodosFilter.All,
14+
};
15+
16+
const todos = createReducer(initialState.todos)
17+
.handleAction(ADD, (state, action) => [...state, action.payload])
18+
.handleAction(TOGGLE, (state, action) =>
19+
state.map(item =>
20+
item.id === action.payload
21+
? { ...item, completed: !item.completed }
22+
: item
23+
)
24+
);
25+
26+
const todosFilter = createReducer(initialState.todosFilter).handleAction(
27+
CHANGE_FILTER,
28+
(state, action) => action.payload
29+
);
30+
31+
export default combineReducers({
32+
todos,
33+
todosFilter,
34+
});

playground/src/features/todos/reducer.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@ import { Todo, TodosFilter } from './models';
55
import * as actions from './actions';
66
import { ADD, CHANGE_FILTER, TOGGLE } from './constants';
77

8-
export type TodosState = {
9-
readonly todos: Todo[];
10-
readonly todosFilter: TodosFilter;
11-
};
12-
138
export type TodosAction = ActionType<typeof actions>;
149

10+
export type TodosState = Readonly<{
11+
todos: Todo[];
12+
todosFilter: TodosFilter;
13+
}>;
14+
const initialState: TodosState = {
15+
todos: [],
16+
todosFilter: TodosFilter.All,
17+
};
18+
1519
export default combineReducers<TodosState, TodosAction>({
16-
todos: (state = [], action) => {
20+
todos: (state = initialState.todos, action) => {
1721
switch (action.type) {
1822
case ADD:
1923
return [...state, action.payload];
@@ -29,7 +33,7 @@ export default combineReducers<TodosState, TodosAction>({
2933
return state;
3034
}
3135
},
32-
todosFilter: (state = TodosFilter.All, action) => {
36+
todosFilter: (state = initialState.todosFilter, action) => {
3337
switch (action.type) {
3438
case CHANGE_FILTER:
3539
return action.payload;

playground/src/store/types.d.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import { StateType, ActionType } from 'typesafe-actions';
2+
13
declare module 'MyTypes' {
2-
import { StateType, ActionType } from 'typesafe-actions';
34
export type Store = StateType<typeof import('./index').default>;
45
export type RootAction = ActionType<typeof import('./root-action').default>;
56
export type RootState = StateType<typeof import('./root-reducer').default>;
67
}
8+
9+
declare module 'typesafe-actions' {
10+
export type RootAction = ActionType<typeof import('./root-action').default>;
11+
}

0 commit comments

Comments
 (0)