NgRx store actions
NgRx Store provides state management for creating maintainable, explicit applications through the use of single state and actions in order to express state changes.
- Why use NgRx Store? Give read here
Actions
- Actions express unique events that happen throughout your application.
- Events like api requests, user interaction with page and son are described using actions.
- Actions help to understand how events are handled in your application.
Action interface
- An Action in NgRx is made up of a simple interface:
interface Action {
type: string;
}
- The
type
property is for describing the action that will be dispatched in an application. - The value of the type comes in the form of
[Source] Event
and is used to provide a context of what category of action it is, and where an action was dispatched from. For example, to delete todo thetype
will be[Todo Component] Delete Todo
where[Todo Component]
isSource
andDelete Todo
isEvent
.
Creating action for todo component
- Before we jump into code, let’s list down what could be action in our todo component
- Adding new todo
- Modifying/updating existing todo (done status: true/false)
- Deleting todo
- Create a new action file viz
todo.actions.ts
insidestore
folder oftodo
to describe todo actions to add, modify and delete createAction()
creates a configuredCreator
function,Creator
function when called returns an object in the shape of theAction
interfacecreateActin()
take two paramstype
which describes the action that will be dispatchedconfig
which defines additional metadata needed for the handling of the action. By default, it is undefined
props
method is used to define any additional metadata needed for the handling of the action.- For example, to
deleteTodo
we need additional info which istodoId
.
- For example, to
- Action creators provide a consistent, type-safe way to construct an action that is being dispatched.
- More here
// todo.actions.ts
import {createAction} from '@ngrx/store';
export const saveOrUpdateTodo = createAction(
'[Todo Component] Update Todo',
props<{todo: Todo, isUpdate: boolean}>()
);
export const deleteTodo = createAction(
'[Todo Component] Delete Todo',
props<{todoId: number}>()
);
Dispatching created action
export class TodoComponent implements OnInit {
undoOrCompleteTodo(item: Todo) {
this.todos = this.todos.map(todo => todo.id === item.id ? {...todo, done: !todo.done} : todo);
const todo: Todo = {...item, done: !item.done};
this.store.dispatch(saveOrUpdateTodo({todo, isUpdate: true}));
}
deleteTodo(todoId: number) {
const todo = this.todos.find(todo => todo.id === todoId);
if (todo) {
this.todos.splice(this.todos.indexOf(todo), 1);
}
this.store.dispatch(deleteTodo({todoId}))
}
addTodo(): void {
if (this.todoIdFormControl.value && this.todoIdFormControl.value >= 0 && !this.todos.find(t => t.id === this.todoIdFormControl.value)) {
const todo: Todo = {
id: this.todoIdFormControl.value,
description: this.todoDescriptionFormControl.value ?? '',
done: false
}
this.todos.push(todo);
this.store.dispatch(saveOrUpdateTodo({todo, isUpdate: false}));
}
}
}
Let’s break down what’s happening. For example, this.store.dispatch(saveOrUpdateTodo({todo, isUpdate: true}))
The saveOrUpdateTodo
action creator receives an object of todo
and isUpdate
and returns a plain JavaScript object with a type
property of [Todo Component] Update Todo
, with todo
and isUpdate
as additional properties.
The JavaScript object will be of following format.
{
type: '[Todo Component] Update Todo',
todo : {id: 123, description : 'First todo', done : true},
isUpdate: true
}
The returned action has a very specific context about where the action came from and what event happened.
- The category of the action is captured within the square brackets [].
- The category is used to group actions for a particular area, whether it be a component page, backend API, or browser API.
- The
Update Todo
text after the category is a description about what event occurred from this action. In this case, the user updated the status fromcompleted
toundo
or vice versa.
You can also write actions using class-based action creators, which was the previously defined way before action creators were introduced in NgRx.