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
.
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 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.
Actions are synchronous
Actions are executed synchronously, therefore, you can immediately query your store to see the updated state.
store.getActions().todos.addTodo('Learn Easy Peasy');
store.getState().todos.items;
// ["Learn Easy Peasy"]
Debugging Actions
Ensure you have the Redux Dev Tools extension installed. This will allow you to see your dispatched actions, with their payload and the effect that they had on your state.
Example
This example demonstrates how to both define an action and consume it from a React component.
import { action, createStore, useStoreActions } from 'easy-peasy';
const store = createStore({
total: 0,
add: action((state, payload) => {
state.total += payload;
})
});
function Add10Button() {
const add = useStoreActions(actions => actions.add);
return <button onClick={() => add(10)}>Add 10</button>;
}
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));
})
};
Don't destructure the state argument
In order to support the mutation API we utilise immer. Under the hood immer utilises Proxies in order to track our mutations, converting them into immutable updates. Therefore if you destructure the state that is provided to your action you break out of the Proxy, after which any update you perform to the state will not be applied.
Below are a couple examples of this antipattern.
addTodo: action(({ items }, payload) => {
items.push(payload);
})
or
addTodo: action((state, payload) => {
const { items } = state;
items.push(payload);
})
Switching to an immutable API
By default we use immer to provide a mutation based API.
This is completely optional, you can instead return new state from your actions like below.
import { action } from 'easy-peasy';
const model = {
todos: [],
addTodo: action((state, payload) => {
// 👇 new immutable state returned
return [...state, payload];
})
}
Should you prefer this approach you can explicitly disable immer
via the disableImmer
configuration value of createStore.
import { createStore } from 'easy-peasy';
const store = createStore(model, {
disableImmer: true // 👈 set the flag
})