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:
-
action
is the action itself. -
dispatchCount
is the sequential number of the dispatch. -
ini
is the observer will actually be called twice for each action dispatch: One withini: true
when the action is dispatched (before the state is changed), and one withini: false
when 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:
-
action
is the action that changed the state. -
prevState
is 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. -
newState
is 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);
-
error
is 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, bothprevState
andnewState
will be the current store state when the error was thrown. -
dispatchCount
is the sequential number of the dispatch.
The state-observer is a good place to add an interface to the Redux DevTools. It's also a good place to logs 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);
}