State and Redux

An interactive project contains many moving pieces that constantly change and oftentimes the UI is expected to reflect these changes. We call the collection of system, environment and project data the “Global State”. This state serves as the one source of truth in order to maintain coherency while developing and running the project.

The Global State is saved in a Redux state. This state includes all data of the player and its plugins and is easily accessible from both the player API as well as within react components in the UI system. Let’s explore how to use the global state while developing UI components.

See the Debugging tutorial to learn how to use Redux devtools.

The store

The store is a global object that is responsible of exposing and changing the state.
A UI component can access whatever it needs from the store by using Redux’s connect() function (for class-based components) or the useSelector() hook (for functional components). These ensure that the component re-renders once the relevant part of the store is changed. They are both accessible through the UI Plugin.
If you need to access the store from outside a UI component, you can do so via the player’s reduxStore property and the getState() method.

In the past, one could access the store using props.globalState, however that use is now considered deprecated and will be removed in future versions of the eko API

At a given point in time the store object could look something like this

// Example: partial defintion.
{
  player: {
    volume: 1,
    muted: false,
    isPlaying: true,
    isPaused: false,
    isSeeking: false,
    ...
  },
  decision: {
    myParentNodeId: {
      parent: 'myParentNodeId',
      children: ['myFirstChild', 'mySecondChild'],
      defaults: ['myFirstChild'],
      state: 'inactive',
      time: 0,
      duration: 7.485011000000002,
      progress: 0,
      remainingTime: 7.485011000000002,
      selected: {}
    },
    ...
  },
  ui: {
    controllers: {
      myParentNodeId: {
        controllerType: 'DECISION',
        visible: false,
        decisionActive: false,
        inLingerTimeout: false
      },
      ...
    }
  },
  sparks: {
    visible: false
  },
  subtitles: {
    visible: false,
    text: 'To be, or not to be, that is the question',
    availableLanguages: {
      en: {
        nativeName: 'English',
        englishName: 'English'
      }
    },
    language: '',
    effectiveLanguage: 'en',
    style: {}
  },
  variables: {
    myVarName: 'myVarValue',
    ...
  }
}

More details about the different sub-objects of the redux globalState can be found here:

Let’s take a look at a real code example.

Code example

Suppose we want to create a UI component for your project which displays the player’s current node time as it changes. We’ll be using currentNodeTime property from player state object in the component’s render() method.

The benefit of doing it this way is that your UI component will re-render automatically only when the current node time value changes so you don’t need to take care of that yourself!
Here is how the code for this component would look like in two flavors, class based and functional:

MyNodeTime.jsx (class-based version)

import React from 'react';
import EkoUIComponents from "EkoUIComponents";

import 'MyNodeTime.scss';

class MyNodeTime extends React.Component {
    render() {             
        let currentNodeTime = this.props.currentNodeTime;
        let currentNodeTimeFormatted = currentNodeTime.toFixed(2);

        return <div className='myNodeTime'>{currentNodeTimeFormatted}</div>;
    }
}
let mapStateToProps = (globalState, ownProps) => ({
    currentNodeTime: globalState.player.currentNodeTime
}) 

export default EkoUIComponents.connect(mapStateToProps)(MyNodeTime);

MyNodeTime.jsx (functional version)

import React from 'react';
import 'MyNodeTime.scss';

const myNodeTime = props => {
    let currentNodeTime = player.ui.useSelector(globalState => globalState.player.currentNodeTime);    
    let currentNodeTimeFormatted = currentNodeTime.toFixed(2);
    return <div className='myNodeTime'>{currentNodeTimeFormatted}</div>;
} 

export default myNodeTime;

MyNodeTime.scss

.myNodeTime{
    position: absolute;
    top: 20px;
    font-size: 30px;
    background-color: orange;
}

The result will be something like this:

Alt Text

Rate this page: X
Tell us more!