Skip to content

Commit b1af97d

Browse files
aboudardAlain Boudard
andauthored
Fixing signatures and payload use (#6)
Co-authored-by: Alain Boudard <alain_boudard@groupe-sma.fr>
1 parent 690a241 commit b1af97d

File tree

4 files changed

+13
-10
lines changed

4 files changed

+13
-10
lines changed

docs/chapter-5.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@ This is an action that adds a category named `Food` to the list of all categorie
2727
```ts
2828
// src/app/state/actions.ts
2929
import { createAction, props } from "@ngrx/store";
30+
import { Category } from "./state";
3031

3132
export const addCategory = createAction(
3233
"[Category List] Add Category",
33-
props<{ name: string }>()
34+
props<{ category: Category }>()
3435
);
3536
```
3637

37-
Let's break down this example. First of all, the name `createAction` is a bit deceptive; it does not create an action; in fact, it creates a function which, when called, wil produce an action object. The first argument is the `type` of the action that will be produced. When called, the `addCategory` function will **always** create an action with type "[Category List] Create Category". The second argument uses the bizarre function `props`, which is a generic function that allows us to define the type of the `payload` which the created action will have. Essentially, it explains that in order to create the action using the `addCategory` function, we should call it and provide an object that has a property `name` which is a `string`. Let's do this and `console.log` the result.
38+
Let's break down this example. First of all, the name `createAction` is a bit deceptive; it does not create an action; in fact, it creates a function which, when called, wil produce an action object. The first argument is the `type` of the action that will be produced. When called, the `addCategory` function will **always** create an action with type "[Category List] Create Category". The second argument uses the bizarre function `props`, which is a generic function that allows us to define the type of the `payload` which the created action will have. Essentially, it explains that in order to create the action using the `addCategory` function, we should call it and provide an object that has a property `category` which is defined in the Interface Category defined in the AppState. Let's do this and `console.log` the result.
3839

3940
```ts
4041
// src/app/app.component.ts
@@ -47,15 +48,15 @@ import { addCategory } from "./state/actions.ts";
4748
})
4849
export class AppComponent implements OnInit {
4950
ngOnInit() {
50-
console.log(addCategory({ name: "Food" }));
51+
console.log(addCategory({category:{ name: "Food" }}));
5152
}
5253
}
5354
```
5455

5556
In the console, we will see the following:
5657

5758
```js
58-
{name: 'Food', type: '[Category List] Add Category'}
59+
{category: {name: 'Food', type: '[Category List] Add Category'}}
5960
```
6061

6162
So essentially, `createAction` provided us with an easy way of creating actions of the same type. `addCategory` in our case is an `ActionCreator`, a function which produces an action object whenever called, and `props` explained what argument that `ActionCreator` function expects.

docs/chapter-6.mdx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ export function reducer(state: AppState, action: Action) {
8989
}
9090
```
9191

92+
> **_NOTE:_** Beware of the **payload** attribute of the Action, which only fits the "old" way to create actions. Next example will use the props and call directly the properties added to the Action.
93+
9294
Now let's deconstruct this example. First of all, as you see, the `reducer` function is a pure function. Next, it differentiates between `Actions` using a `switch` statement and the `Action` types. On each different `Action`, our `Reducer` will return a slightly different version of a `State` to match that `Action`'s intent. As we have only one `Action` for now, we wrote only one `case` statement, and if the `Action` is not identified, we return the `state` without modifying it. As you can see, the practice is to copy the state, and return a new object. **Never modify the previous `state` object, always return a new one**.
9395
What will happen is when we dispatch the `addCategory` `Action`, the `reducer` function will be called, the new `State` (with the added category object in the `categories` array) will be returned, and then NgRx will propagate that change to all of our components.
9496

@@ -106,9 +108,9 @@ import { AppState, initialState } from "./state";
106108

107109
const _reducer = createReducer(
108110
initialState,
109-
on(actions.addCategory, (state, action) => ({
111+
on(actions.addCategory, (state, {category}) => ({
110112
...state,
111-
categories: [...state.categories, action.payload],
113+
categories: [...state.categories, category],
112114
}))
113115
);
114116

@@ -117,7 +119,7 @@ export function reducer(state: AppState, action: Action) {
117119
}
118120
```
119121

120-
Let's understand what goes on here. First of all, we have the `createReducer` function, which does exactly that - creates the resulting `Reducer` function. It receives the initial state of the application, and then an arbitrary amount of handlers for each action (we provided only one, but we can (and will!) have lots of `Actions`, and thus. lots of `on` function calls). The `on` function receives an `Action` as the first argument, and a callback function as a second one, which acts like a mini-`Reducer` for only that specific `Action`. Thus, the need to write huge `switch` statements is eliminated. Now let's understand two specific parts of this code:
122+
Let's understand what goes on here. First of all, we have the `createReducer` function, which does exactly that - creates the resulting `Reducer` function. It receives the initial state of the application, and then an arbitrary amount of handlers for each action (we provided only one, but we can (and will!) have lots of `Actions`, and thus. lots of `on` function calls). The `on` function receives an `Action` as the first argument, and a callback function as a second one, which acts like a mini-`Reducer` for only that specific `Action`. Thus, the need to write huge `switch` statements is eliminated. Note that the callback signature is (state, {category}) in which we [destruct](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#object_destructuring) the action passed in the second argument in order to only get the props. Now let's understand two specific parts of this code:
121123

122124
1. _Why did we create a `_reducer` function only to call it in the `reducer` function?_ The thing is, in the next chapters we are going to register the `reducer` function in our `AppModule`, and because of how the Angular build process works, it does not allow including anonymous functions like the one returned by `createReducer`, only declared named functions. Thus we wrote a conventional function and called our `_reducer` from it. **Always create `Reducers` like this**.
123125
2. _Why didn't we `import` the `Action` we wanted by name, but rather did `import * as actions from './actions';`? Because we can easily have dozens of `Actions` that are handled in a single `Reducer` function, we would like to import them as a single object to avoid too cluttered imports.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
const _reducer = createReducer(
22
initialState,
33
// other handlers
4-
on(deleteCategory, (state, action) => ({
4+
on(actions.deleteCategory, (state, {name}) => ({
55
...state,
66
categories: state.categories.filter(
7-
(category) => category.name !== action.payload
7+
(cat) => cat.name !== name
88
), // filter out the deleted category
99
}))
1010
);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const _reducer = createReducer(
22
initialState,
33
// other handlers
4-
on(deleteAllCategories, (state, action) => ({ ...state, categories: [] })) // just assign a new empty array for categories
4+
on(actions.deleteAllCategories, (state) => ({ ...state, categories: [] })) // just assign a new empty array for categories
55
);

0 commit comments

Comments
 (0)