State Stack

The Jovo $state stack remembers components that are relevant for the current user session.

Introduction

The $state stack is a key element of routing and dialogue management as part of the dialogue and logic step of the RIDR Lifecycle.

$state is stored in session data with the following characteristics:

  • It is an array of components that the user was interacting with in this session
  • It also stores information about component delegation, for example which handlers to resolve to
  • Additionally, it can hold component data which gets cleared once the component resolves

This information is important for the router to understand where in the conversation the user currently is. For example, if the user says "yes", the Jovo app needs to know which question they are referring to. If you want to dive deeper, take a look at this comprehensive introduction to dialogue management.

If you're used to building state machines (for example Jovo v3), you can see a Jovo component as a state. The main difference is that the state is now a stack, making it easier to keep track of previous interactions.

Once a component is entered, it is added to the Jovo $state stack:

$state = [
  {
    component: 'SomeComponent',
  },
];

The component is removed from the stack once it resolves or the session closes.

Stacking Components

The Jovo $state stack is an array, its last index ($state.length - 1) referencing the most recently active component.

This is helpful to remember previous steps, for example after a component delegated to a different one to complete a task.

As an example, let's start with a component called TableReservationComponent that gets invoked by a user request and is thus added to the $state:

$state = [
  {
    component: 'TableReservationComponent',
  },
];

The TableReservationComponent then needs to collect some information and delegates to a CollectNumberOfPeopleComponent. That component can either resolve with a success or an exit event. The delegating component references handlers to be called for each of these events to the $delegate() call:

return this.$delegate(CollectNumberOfPeopleComponent, {
  resolve: {
    success: confirmTableAvailability,
    exit: offerHelp,
  },
});

The delegate call then enters the START handler inside CollectNumberOfPeopleComponent and adds the relevant information for the next request to the $state:

$state = [
  {
    component: 'TableReservationComponent',
  },
  {
    component: 'CollectNumberOfPeopleComponent',
    resolve: {
      success: 'confirmTableAvailability',
      exit: 'offerHelp',
    },
  },
];

There are now several things that could happen in CollectNumberOfPeopleComponent:

  • It delegates to another component, adding that one to the $state as well
  • It collects the information and then resolves, which results in its removal from the $state

If the CollectNumberOfPeopleComponent gets removed, the router looks if there are any components left. In this case it's TableReservationComponent. Here, the router invokes the right handler based on the event coming from the $resolve().

State Properties

Each object in the $state array contains at least a component string which references the path to a component. Subcomponents are nested using ., e.g. ParentComponent.SubComponent.

The object may also include: