Skip to main content

ReactObservableValue

React hooks for ObservableValue: read a value and rerender, select part of it, or follow it with a binding.

Full API reference at jdevon.github.io/react-observable-value.

Install​

badges/github badges/wally

Install with Wally by adding both packages to your wally.toml:

[dependencies]
ObservableValue = "jdevon/observable-value@0.1.0"
ReactObservableValue = "jdevon/react-observable-value@0.1.0"

ReactObservableValue only provides the hooks; you create values with ObservableValue. Wally installs a single copy of ObservableValue when both versions are compatible, so the hooks and your code share the same class.

Usage​

local React = require(ReplicatedStorage.Packages.React)
local ObservableValue = require(ReplicatedStorage.Packages.ObservableValue)
local ReactObservableValue = require(ReplicatedStorage.Packages.ReactObservableValue)

local health = ObservableValue.new(100)

local function HealthLabel()
-- rerenders whenever health changes
local currentHealth = ReactObservableValue.useObservableValue(health)

return React.createElement("TextLabel", {
Text = `{currentHealth} HP`,
})
end

health:set(80) -- every mounted HealthLabel now shows 80 HP

The second return value increases every time the value changes. Use it as a dependency when a table value may have been changed in place:

local inventory = ObservableValue.new({} :: { [string]: number })

local function ItemCount()
local items, updateId = ReactObservableValue.useObservableValue(inventory)

local total = React.useMemo(function()
local count = 0
for _, quantity in items do
count += quantity
end
return count
end, { updateId })

return React.createElement("TextLabel", {
Text = `{total} items`,
})
end

For values that change often, useObservableBinding follows the value with a React binding instead of rerendering:

local progress = ObservableValue.new(0)

local function ProgressBar()
local progressBinding = ReactObservableValue.useObservableBinding(progress)

return React.createElement("Frame", {
Size = progressBinding:map(function(value)
return UDim2.fromScale(value, 1)
end),
})
end

To rerender only when part of a value changes, useObservableSelector returns what your selector picks out:

local blurStack = ObservableValue.new({} :: { [number]: true })

local function BlurOverlay()
-- rerenders only when the stack goes from empty to not empty, or back
local isVisible = ReactObservableValue.useObservableSelector(blurStack, function(stack)
return next(stack) ~= nil
end)

return React.createElement("Frame", {
Visible = isVisible,
})
end

Results are compared with ==. Pass an isEqual function as the third argument if your selector builds a new table each time.

API​

Each member links to its full entry in the API reference.

Functions​

  • .useObservableValue - returns an ObservableValue's current value and an update id, and rerenders the component whenever the value changes.
  • .useObservableBinding - returns a React binding that follows an ObservableValue without rerendering
  • .useObservableSelector - returns part of an ObservableValue and rerenders only when that part changes