useReactiveValue()

Storing Primitives

The useReactiveValue hook combined with the ReactiveValue<T> C# class allows you to hook into individual properties of an object.

ReactiveValue’s are set via the .Value property, which will automatically trigger any watching hooks.

For example, this Player MonoBehaviour could be stored in Globals:

// C#
public class Player: MonoBehaviour {

public ReactRendererBase renderer;

// special class for communicating between React and Unity
public ReactiveValue<float> Score = new ReactiveValue<float>();
public ReactiveValue<int> Deaths = new ReactiveValue<int>();
public ReactiveValue<int> Kills = new ReactiveValue<int>();

void Awake() {
renderer.Globals["Player"] = this;
}

// set ReactiveValue with .Value
public void AddScore(float score) {
Score.Value += score;
}

/* Rest of Player code */
}

And then accessed in React via the hook:

// JS
export function ScoreTracker() {
const {Player} = useGlobals();
// will trigger a re-render when Score changes
const score = useReactiveValue(Player.Score);

return (<text>{score}</text>)
}

Non-Primitive Types

For Lists and Dictionaries, there are the ReactiveList<T> and ReactiveRecord<T> classes.

Normal ReactiveValue’s can also hold non-primitive objects, but they will only trigger a re-draw if the actual object reference changes.

It’s recommended to either use a ReactiveValue per field (see above), an immutable type like record, or a secondary variable for tracking the state change (see below).

Method 1: ReactiveList

Say we want to store a Player’s inventory for drawing on the UI. The simplest approach is to use a ReactiveList:

// C#
/* Player class continued */
{
// track a list of all items in the inventory
public ReactiveList<Item> Inventory = new ReactiveList<Item>();

public void AddItem(Item item) {
// will trigger re-renders as needed
Inventory.Add(item);
}
}
// JS
export function DrawItems() {
const {Player} = useGlobals();
const itemList = useReactiveValue(Player.Inventory);
// return drawn items
}

Method 2: Secondary Variable

Now we want to add more functionality to the inventory, and it can no longer be just a list. Instead it is an Inventory object, and we track changes to its internal state with a counter variable.

/* Player class continued */
{
public ReactiveValue<int> InventoryChanges = new ReactiveValue<int>();
public InventorySystem Inventory;

public void AddItem(Item item) {
// trigger re-render with counter instead
InventoryChanges.Value += 1;
Inventory.Add(item);
}
}
// JS
export function DrawItems() {
const {Player} = useGlobals();

// re-render based on the counter
const _invChanges = useReactiveValue(Player.InventoryChanges);

// reference the Inventory object and call functions
const inventory = Player.Inventory;
const itemList = inventory.ListItems();

// call other functions from Inventory class

// return drawn items
}