React Data Binding

React with Prysm Data Binding

React’s data binding can be replaced with the native data binding in Prysm which can improve performance.

The dynamic data binding in Prysm re-evaluates data-bind-* attributes on elements which values can change or if attributes are added or removed from that elements. Also if new elements get added to the page with those attributes.

React Support: JS React support

Dynamic Data Binding overview

Sample Overview

The page shows a list of items that are interactable. The list itself is generated by React. Upon clicking on an item, the price is subtracted from the total amount of Credits and the item is bought if there are enough Credits. The price element gets replaced with another “Owned” element. If there are not enough Credits to buy some of the items, the price element receives an additional CSS class that colors the price in red. Next to the “Unlock Avatars” text, there is a button that will remove the already bought items.

Everything except for the generation of the list of items is done with data-bind-* attributes within React Components.

In this example, the use of the React state is replaced with the Prysm data binding which is optimized natively. Prysm data binding handles changes to the model and updates it while React is used only once to populate/re-generate a list of items.

Running the Sample

To run the sample you need to:

  • Install the required npm packages
  • Configure Prysm to load from the destination.
  1. Navigate to the sample folder which is included in the package in \Samples\uiresources\ReactDataBinding folder.

  2. Open a CLI in the folder and run npm install.

  3. Then run npm run watch which will start a watch server on http://localhost:3000 or build which will build the sample in the dist folder.

  4. In the package root folder locate the Player.bat file, open it in edit mode and set --url= to load http://localhost:3000. When starting the Player.bat, it will start directly from that URL and load the page from it every time.

You can alternatively drag and drop the index.html from the dist folder after launching the Player from Player.bat.

Sample Specifics

The first step is to include cohtml.js which is in the src folder and import the required model which is created using engine.createJSModel('Player', modelDataObject)

Rendering of the main App should be done after ensuring that the Player model has been created.

engine.whenReady.then(() => {
 ReactDOM.render(<App />, document.getElementById("root"));
});

The App will render the AvatarList Component which needs 2 things - the model object that is available globally and the array of items to be populated as a string:

<AvatarList
 model={Player}
 modelpropitems={'Player.inventory'}>
</AvatarList>

The AvatarList Component will synchronize the models once upon mounting.

Since the model is now passed to the Component, the array of items / <Avatar>s can be generated:

const inventoryItems = Inventory.map(
 (item, i) => (
   <Avatar key={i}
           index={i}
           modelitem={item}
           model={model}
           {...rest}
   ></Avatar>
 ));

and then render the items themselves from React:

<div className={'avatar-list'}>
 {inventoryItems}
</div>

Now everything needed for the Avatar Component to be used with data-bind-* attributes is available through its props.

For example to set the name of the avatar and show the “Owned” label when an Avatar is bought:

<div
 className={'avatar-name'}
 data-bind-value={`{{${props.modelpropitems}}}[${props.index}].avatar.name`}
 ></div>

<div
 className={'avatar-owned-label'}
 data-bind-if={`{{${props.modelpropitems}}}[${props.index}].avatar.owned`}
>Owned</div>

The modelpropitems was passed from <AvatarList>.

Force render AvatarList Component

Since React renders the list of items and it doesn’t know when the Player model gets changed, a force rendering of that component is required.

A custom hook is created in a separate file so it can be imported/reused where it is needed:

export const useForceRender = () => {
 const [, setRerender] = useState('');

 // Dummy - force re/render
 const setForceRender = () => {
   setRerender({});
 };

 return setForceRender;
};

Now, to filter all items that are not “owned” from the Player model’s inventory array and re-render the List component:

const removeOwned = () => {
 // Use filter method so React recognizes that there is a need for update.
 model.inventory = Inventory.filter(((el)=>!el.avatar.owned));
 cohtmlModelUpdater.updateModel(model);
 forceRender();
};

Now, this can be called by clicking the “Remove Owned from Altar” button.