Actions
Group state changes behind named functions, return values, call other actions, and extend a store from outside.
The problem this solves
Free-form setState calls scatter the rules for changing state across components. Actions give each change a
name and keep the rules in one place. They are optional.
Define actions on a store
Each action is a function that returns a function. The outer function takes your arguments. The inner one receives the store tools.
import { createGlobalState } from 'react-global-state-hooks';
export interface Todo {
id: number;
text: string;
done: boolean;
}
export const useTodos = createGlobalState(
{ todos: [] as Todo[], nextId: 1 },
{
actions: {
add(text: string) {
return ({ setState }) => {
setState((state) => ({
todos: [...state.todos, { id: state.nextId, text, done: false }],
nextId: state.nextId + 1,
}));
};
},
toggle(id: number) {
return ({ setState }) => {
setState((state) => ({
...state,
todos: state.todos.map((todo) => (todo.id === id ? { ...todo, done: !todo.done } : todo)),
}));
};
},
// Actions can return values, and can call the store's other actions through `actions`.
addMany(texts: string[]) {
return ({ actions }) => {
texts.forEach((text) => actions.add(text));
return texts.length;
};
},
},
},
);The tools passed to the inner function are setState, getState, setMetadata, metadata, getMetadata,
subscribe and actions (the store’s own action object).
In components, the hook’s second item is the actions object:
const [todos, actions] = useTodos((state) => state.todos);
actions.add('Write docs');Outside React use useTodos.actions.add(...). Whatever an action returns is returned to the caller, so an async
action returns a promise.
Extend a store from outside
actions(store, config) binds an extra group of actions to a store you did not define, or to keep a file
small. Actions in the same group call each other with this.
import { actions, createGlobalState } from 'react-global-state-hooks';
export const useCounter = createGlobalState(1);
// Extend any store with extra actions without touching its definition.
export const counterActions = actions(useCounter, {
double() {
return ({ setState, getState }) => {
setState(getState() * 2);
};
},
// Actions in the same group call each other with `this`.
quadruple() {
return () => {
this.double();
this.double();
};
},
});actions<API>()({...}) (a template) lets you define actions before the store exists and bind them later with
template(api). That is how you attach internal actions in onInit. See the
TypeScript page for how to get API.