Diagram Maker’s Action Interceptor

2021.08.27

Introduction

Action Interceptor is an optional hook on Diagram Maker's events system. It is called right before the action is dispatched to the store allowing for control, modification and even cancelling the whole action.

Basics

In previous post I described how to include Diagram Maker into VueJs's project, and today we will continue on that project. Initialization of diagram takes place in Vue's mounted hook. In the second argument for DiagramMaker is a configuration object which is where we define the actionInterceptor.

mounted () {
    this.diagram = new DiagramMaker(
      this.$refs['diagramRoot'],
      {
        actionInterceptor: (action, dispatch, getState) => {
          const state = getState()

          .
          .
          .

          dispatch(action)
        }
      }
    )
}

Action interceptor takes 3 arguments: action, dispatch and getState. First is self explanatory, it is an redux action to be dispatched to the store. Second is dispatch function, if you implement actionInterceptor in your project you are responsible for dispatching it. Third is getState function that gets you current state of the store.

Cancelling Action

The library provides DiagramMakerActions constant that contains all the actions which will help us control creating edge and node actions. When an edge is created, there is EDGE_CREATE action dispatched. In our example we will allow to create only one edge for one node. In example below we retrieve the current state to look into already existing edges. We implement switch statement in action's type and when action is of type EDGE_CREATE we iterate over edges in the store and search the one which starts at the node that newly created edge is starting as well. If that's the case we call return which effects in action being cancelled. Right before the return we can inform the user of the limitation we've just implemented.

import { DiagramMakerActions } from 'diagram-maker'

...

actionInterceptor: (action, dispatch, getState) => {
  const state = getState()

  switch (action.type) {
    case DiagramMakerActions.EDGE_CREATE:
      for (edge in state.edges) {
          if action.payload.src === edge.src {
              alert('only one edge per node')
              return
          }
      }
    case DiagramMakerActions.NODE_CREATE:
      ...
      break
  }

  dispatch(action)
}

Dispatching another action

While one action is about to be dispatched, we can dispatch another one. Below we count the nodes in our diagram and save it in the store. Notice that once we dispatch new action we do not call return which allows for the NODE_CREATE action to be still dispatched to the store.

actionInterceptor: (action, dispatch, getState) => {
  switch (action.type) {
    const state = getState()
    case DiagramMakerActions.EDGE_CREATE:
      ...
      break
    case DiagramMakerActions.NODE_CREATE:
      const newCount = Object.keys(state.nodes).length + 1
      const newAction = {
          payload: {
              count: newCount
          },
          type: 'COUNT_NODES'
      };
      dispatch(newAction);
      break
  }
  dispatch(action)
}

Ansynchronous actions

Diagram maker allows async actions as well.

actionInterceptor: (action, dispatch, getState) => {
   setTimeout(
    () => {
      dispatch({
        type: 'ASYNC_ACTION'
      });
    },
    1000);
  dispatch(action);
}

Conclusion

Diagram Maker's Action Interceptor allows you to take control over it's logic. Every action can be cancelled right before it is dispatched allowing for example limiting number of edges coming from each node. You can dispatch complete new action on top of other one and keep count of nodes in the diagram or any arbitrary data you need. You can also log the current state during action dispatch for debugging.