Skip to main content

Understanding State

One of the biggest differences between the current Threekit player and the new Headless module lies in how state is managed.

Threekit Player

Player State Diagram

In the Threekit Player, everything is set up with global shared state. These redux slices are initialized on startup, and then all state is managed inside the store. For example, when we fetch an asset, the result of the fetch is that the cas store stores the cas state for that asset (objects, refs), and then the content is checked out into the sceneGraph store (api._store.get("sceneGraph")), (scenes, nodes).

Then, once the asset has been initialized, any changes will directly modify this mutable state. For example, sceneGraph.set will directly manipulate the node. Any asset that is referenced will get fetched, and cloned so that each reference of that asset is unique.

This mutable state then has a barrier to using it within react. For example, any changes to the Player instance should increment the private _v property, which should cause the react embed to rerender.

Threekit Headless

The new approach eschews the global shared state completely.

Headless State Diagram

In the new module, asset state is local to the Asset component. On initialization, we fetch from cas-service, and insert the result into AssetState.

export interface AssetState {
id: string;
orgId?: string;
objects: Objects;
refs: Refs;
commits: { [hash: string]: any }; // make a Commit interface
nodes: SceneGraphNodeMap;
attributes: AttributesMap;
}

This state is made immutable with immer.js.

The difference is made stark when we look at what isn't required with this new approach (looking just at state management here):

  1. Component re-rendering is straightfoward, since we are passing immutable objects through our props hierarchy. (No more ._v!).
  2. The checked out asset lies right beside the cas objects, and contains just that asset's data. No digging through thousands of nodes.
  3. Caching is done through useLoader, so referencing the same asset multiple times will not require a fetch/checkout, but each asset will have a unique copy.
  4. No asset instances!. Cloning, maintaining separate references for instance id's vs the original asset id goes away.
  5. Unloading assets means unmounting the component, and the state disappears with it.

Immutable means api changes

With the new approach comes some differences. Specifically, because we are working with an immutable state structure, we cannot simply modify the state in place. Any modification to a node, for example, will return an entirely new state structure.

// pass input state in to our modifying function, get a new state out.
const outputState = set(state, [id, 'name'], 'New Node Name');

We have

Internal or External Managed State