Log and Metrics
When you create your store, you can optionally pass it an actionObservers and
a stateObserver, which may be used for logging and collecting metrics for your app:
const store = createStore<State>({
initialState: State.initialState,
actionObserver: actionObserver, // Here!
stateObserver: stateObserver, // Here!
});
actionObserver
The actionObserver is a function which is notified of any action dispatching.
Its parameters are:
-
actionis the action itself. -
dispatchCountis the sequential number of the dispatch. -
iniis the observer will actually be called twice for each action dispatch: One withini: truewhen the action is dispatched (before the state is changed), and one withini: falsewhen the action finished dispatching (after the state is changed).
The action-observer is a good place to log which actions are dispatched by your application. For example, the following code logs actions to the console in development and test modes, but saves metrics in production mode:
function actionObserver(action, dispatchCount, ini) {
if (inDevelopment() || inTests()) {
if (ini) console.log('Action dispatched: ${action}');
else console.log('Action finished: ${action}');
} else {
if (ini) saveMetrics('Dispatched: ${action}');
}
}
Note the saveMetrics function above is a placeholder for your actual metrics saving function.
stateObserver
The stateObserver is a function called for all dispatched actions,
right after the reducer returns, before the action's after() method,
before the action's wrapError(), and before the globalWrapError().
Its parameters are:
-
actionis the action that changed the state. -
prevStateis the state right before the new state returned by the reducer is applied. Note this may be different from the state when the action was dispatched. -
newStateis the state returned by the reducer. Note: If you need to know if the state was changed or not by the reducer, you can compare both states:let ifStateChanged = (prevState !== newState); -
erroris null if the action completed with no error. Otherwise, will be the error thrown by the reducer (before any wrapError is applied). Note that, in case of error, bothprevStateandnewStatewill be the current store state when the error was thrown. -
dispatchCountis the sequential number of the dispatch.
The state-observer is a good place to log actions and state, and collect metrics. For example:
function stateObserver(action, prevState, newState, error, dispatchCount) {
saveMetrics(action, newState, error);
}
If we want to improve the saved metrics, our actions have a log() function that
can be used to log extra information. For example:
class LoadUser extends Action {
async reduce() {
let user = await loadUser();
this.log('User', user.id); // Here!
return (state) => state.copy({user: user});
}
}
And then, the state observer can read and use the action log:
function stateObserver(action, prevState, newState, error, dispatchCount) {
let actionLog = action.getLog(); // Here!
saveMetrics(action, actionLog, newState, error);
}