# action
Allows you to declare an action on your model. An action is used to perform updates on your store.
addTodo: action((state, payload) => {
state.items.push(payload);
});
You can mutate the state directly within an action to update your store -
mutations are turned into immutable updates against your store via
immer
(opens new window).
When you dispatch an action it will be executed synchronously in order to update your store.
# API
An action
is a function that is described below.
# Arguments
handler
(Function, required)The handler for your action. It will receive the following arguments:
state
(Object)The part of the state tree that the action is bound against. You can mutate this state value directly as required by the action. Under the hood we convert these mutations into an update against the Redux store.
payload
(any)The payload, if any, that was provided to the action when it was dispatched.
# Tutorial
# Debugging Actions
Ensure you have the Redux Dev Tools (opens new window) extension installed. This will allow you to see your dispatched actions, with their payload and the effect that they had on your state.
# Using console.log within actions
Despite the Redux Dev Tools extension being available there may be cases in
which you would like to perform a console.log
within the body of your
actions to aid debugging.
If you try to do so you may note that a Proxy
object is printed out instead of
your expected state. This is due to us using immer
under the hood, which
allows us to track mutation updates to the state and then convert them to
immutable updates.
To get around this you can use the debug utility.
import { debug } from 'easy-peasy';
const model = {
myAction: action((state, payload) => {
console.log(debug(state));
}),
};
# Bad Practices
There are few important points to make in the context of actions.
# 1. Don't destructure the state
argument
action((state, payload) => {
const { todos } = state;
// 👆 destructuring the state argument is bad, m'kay
todos.push(payload);
}),
or
action(({ todos }, payload) => {
// 👆 destructuring the state argument is bad, m'kay
todos.push(payload);
}),
Doing this will break our ability to convert your code into an immutable update and will result in your state not being updated.
If you are interested in why this happens; it is because Immer uses proxies (opens new window) to track which state is being mutated/updated. Destructuring breaks out of the proxy, thereby removing this ability and will result in your state not being updated as expected.
# 2. Don't execute any side effects within your action
Actions should remain synchronous and pure. They should only perform state updates and must not do things like making an API request.
action(({ todos }, payload) => {
// 👇 side effects in actions are bad, m'kay
fetch('/todos').then(response => response.json()).then(data => {
state.todos = state.todos.concat(data);
});
}),
If you need to perform side effects then you should encapsulate them within a Thunk, a concept we will introduce later in the tutorial.