State API
API reference for Rex's state management system with universal reactivity and helpers.
Rex provides a comprehensive state management system with universal reactivity. All state objects automatically update UI elements with intelligent type conversion.
Rex.useState<T>(initialValue: T): RexState<T>
Creates a basic reactive state object with helper methods.
Parameters:
initialValue: T
— The initial value of the state
Returns: RexState<T>
with methods:
Core Methods
get(): T
— Returns the current valueset(value: T): ()
— Sets a new value and triggers updatesupdate(updateFn: (current: T) -> T): ()
— Updates value using functiononChange(callback: (newValue: T, oldValue: T) -> ()): () -> ()
— Listens for changes, returns disconnect functionmap<U>(mapFn: (value: T) -> U): U
— Transforms the value for UI bindingeach(mapFn: (item: any, index: number) -> any): any
— Maps over array items for list rendering
Numeric State Helpers
increment(amount?: number): ()
— Adds 1 or specified amount to numeric statedecrement(amount?: number): ()
— Subtracts 1 or specified amount from numeric state
Boolean State Helpers
toggle(): ()
— Flips boolean state value
Array State Helpers
push(...items: any[]): ()
— Adds one or more items to the end of arraypop(): any
— Removes and returns the last item from arrayremoveAt(index: number): ()
— Removes item at specified indexremove(value: any): ()
— Removes first occurrence of valueclear(): ()
— Removes all items from arrayeach(mapFn: (item: any, index: number) -> any): any
— Maps over array items for reactive list rendering
Object State Helpers
setPath(path: string, value: any): ()
— Sets nested property using dot notationgetPath(path: string): any
— Gets nested property using dot notation
Array Example:
local items = Rex.useState({"apple", "banana"})
-- Enhanced array helpers
items:push("cherry") -- ["apple", "banana", "cherry"]
items:push("date", "elderberry") -- Add multiple
local last = items:pop() -- Returns "elderberry", array: ["apple", "banana", "cherry", "date"]
items:removeAt(2) -- Remove index 2: ["apple", "cherry", "date"]
items:remove("cherry") -- Remove by value: ["apple", "date"]
items:clear() -- []
-- Enhanced list rendering with :each()
local listUI = Rex("ScrollingFrame") {
children = {
Rex("UIListLayout") {},
items:each(function(item, index) -- Simplified syntax for reactive lists!
return Rex("TextLabel") {
Text = `{index}: {item}`,
key = item -- Important: Use keys for efficient reconciliation
}
end)
}
}
Advanced List Rendering with :each():
local todos = Rex.useState({
{id = 1, text = "Learn Rex", completed = false},
{id = 2, text = "Build UI", completed = true}
})
-- Complex list rendering with reactive properties
Rex("ScrollingFrame") {
children = {
Rex("UIListLayout") {},
todos:each(function(todo, index)
return Rex("Frame") {
Size = UDim2.new(1, 0, 0, 40),
BackgroundColor3 = todo.completed
and Color3.fromRGB(100, 255, 100) -- Green for completed
or Color3.fromRGB(255, 255, 255), -- White for pending
key = tostring(todo.id), -- Use stable ID as key
children = {
Rex("TextLabel") {
Text = todo.text,
TextStrikethrough = todo.completed
},
Rex("TextButton") {
Text = "✓",
onClick = function()
todos:update(function(currentTodos)
local newTodos = table.clone(currentTodos)
newTodos[index].completed = not newTodos[index].completed
return newTodos
end)
end
}
}
}
end)
}
}
getPath(path: string): any
— Gets nested property using dot notation
Example:
local count = Rex.useState(0)
-- Get current value
local currentCount = count:get() -- 0
-- Enhanced helpers
count:increment() -- count is now 1
count:increment(5) -- count is now 6
count:decrement() -- count is now 5
-- Set new value
count:set(10)
-- Update based on current value
count:update(function(current) return current + 1 end)
-- Universal reactive binding
local element = Rex("TextLabel") {
Text = count -- Automatically converts number to string!
}
Array State Example:
local items = Rex.useState({"apple", "banana"})
-- Enhanced array helpers
items:push("cherry") -- ["apple", "banana", "cherry"]
items:push("date", "elderberry") -- Add multiple
local last = items:pop() -- Returns "elderberry", array: ["apple", "banana", "cherry", "date"]
items:removeAt(2) -- Remove index 2: ["apple", "cherry", "date"]
items:remove("cherry") -- Remove by value: ["apple", "date"]
items:clear() -- []
-- Enhanced list rendering
local listUI = Rex("ScrollingFrame") {
children = {
Rex("UIListLayout") {},
items:each(function(item, index) -- Simplified syntax!
return Rex("TextLabel") {
Text = item,
key = item -- Automatic reconciliation
}
end)
}
}
Object Path Example:
local user = Rex.useState({
name = "Player",
settings = { theme = "dark", volume = 0.8 }
})
-- Enhanced path operations
user:setPath("settings.theme", "light") -- Deep property setting
user:setPath("settings.volume", 0.5)
local theme = user:getPath("settings.theme") -- Returns "light"
local volume = user:getPath("settings.volume") -- Returns 0.5
Rex.useDeepState<T>(initialValue: T): RexState<T>
Creates a deep reactive state for nested objects. Changes to nested properties trigger reactivity.
Parameters:
initialValue: T
— The initial nested object/table
Returns: RexState<T>
(same interface as useState
)
Example:
local user = Rex.useDeepState({
name = "Player",
stats = {
level = 1,
health = 100,
mana = 50
},
inventory = {
coins = 100,
items = {"sword", "potion"}
}
})
-- Deep updates trigger reactivity
user:update(function(current)
local newUser = table.clone(current)
newUser.stats.level = current.stats.level + 1
newUser.stats.health = 100 -- Reset health on level up
return newUser
end)
-- Bind to nested properties
local healthBar = Rex("Frame") {
Size = user:map(function(u)
local healthPercent = u.stats.health / 100
return UDim2.new(healthPercent, 0, 1, 0)
end)
}
Rex.useComputed<T>(computeFn: () -> T, dependencies: {RexState<any>}, memoKey: string?): RexState<T>
Creates a computed state that automatically updates when dependencies change.
Parameters:
computeFn: () -> T
— Function that computes the derived valuedependencies: {RexState<any>}
— Array of state dependencies to watchmemoKey: string?
— Optional memoization key
Returns: RexState<T>
(read-only)
Example:
local firstName = Rex.useState("John")
local lastName = Rex.useState("Doe")
-- Basic computed state
local fullName = Rex.useComputed(function()
return firstName:get() .. " " .. lastName:get()
end, {firstName, lastName})
-- Memoized expensive computation
local items = Rex.useState({...}) -- Large array
local sortedItems = Rex.useComputed(function()
local list = items:get()
table.sort(list, function(a, b) return a.name < b.name end)
return list
end, {items}, "sortedItemsCache")
print(fullName:get()) -- "John Doe"
firstName:set("Jane")
print(fullName:get()) -- "Jane Doe" (automatically updated)
Rex.useAutoComputed<T>(computeFn: () -> T): RexState<T>
Creates a computed state with automatic dependency tracking.
Parameters:
computeFn: () -> T
— Function that computes the value
Returns: RexState<T>
(read-only)
Example:
local x = Rex.useState(10)
local y = Rex.useState(20)
local z = Rex.useState(30)
-- Dependencies automatically tracked
local sum = Rex.useAutoComputed(function()
return x:get() + y:get() + z:get() -- x, y, z automatically tracked
end)
print(sum:get()) -- 60
x:set(15)
print(sum:get()) -- 65 (automatically recalculated)
Rex.useAsyncState<T>(asyncFn: () -> T, dependencies: {RexState<any>}?): AsyncState<T>
Creates an async state for handling asynchronous operations with loading and error states.
Parameters:
asyncFn: () -> T
— Async function that returns the datadependencies: {RexState<any>}?
— Optional dependencies that trigger reload
Returns: AsyncState<T>
Example:
local userId = Rex.useState(123)
local userData = Rex.useAsyncState(function()
-- Simulate API call
wait(1)
return {
name = "Player" .. userId:get(),
level = math.random(1, 50),
coins = math.random(100, 1000)
}
end, {userId}) -- Reload when userId changes
-- Use in UI
local userDisplay = Rex("Frame") {
children = userData.loading:map(function(isLoading)
if isLoading then
return { Rex("TextLabel") { Text = "Loading..." } }
end
local error = userData.error:get()
if error then
return { Rex("TextLabel") {
Text = `Error: {error}`,
TextColor3 = Color3.fromRGB(255, 100, 100)
} }
end
local data = userData.data:get()
if data then
return {
Rex("TextLabel") { Text = data.name },
Rex("TextLabel") { Text = `Level {data.level}` },
Rex("TextLabel") { Text = `{data.coins} coins` }
}
end
return {}
end)
}
-- Manual reload
userData.reload()
Rex.batch(updateFn: () -> ()): ()
Batches multiple state updates into a single UI update for performance.
Parameters:
updateFn: () -> ()
— Function containing multiple state updates
Example:
local x = Rex.useState(1)
local y = Rex.useState(2)
local z = Rex.useState(3)
-- Without batching: 3 separate UI updates
x:set(10)
y:set(20)
z:set(30)
-- With batching: single UI update
Rex.batch(function()
x:set(100)
y:set(200)
z:set(300)
end)
Context API
Rex.createContext<T>(defaultValue: T): RexContext<T>
Creates a new context for sharing state across components.
Parameters:
defaultValue: T
- Default value when no provider is found
Returns: RexContext<T>
object
Example:
local ThemeContext = Rex.createContext({
primary = Color3.fromRGB(70, 130, 255),
background = Color3.fromRGB(30, 30, 40),
text = Color3.new(1, 1, 1)
})
Rex.useContext<T>(context: RexContext<T>): T
Consumes a context value within a component.
Parameters:
context: RexContext<T>
- The context to consume
Returns: T
- Current context value
Example:
local function ThemedButton()
local theme = Rex.useContext(ThemeContext)
return Rex("TextButton") {
BackgroundColor3 = theme.primary,
TextColor3 = theme.text
}
end
Rex.withContext<T>(context: RexContext<T>, value: T, fn: () -> any): any
Provides a context value for a function execution.
Parameters:
context: RexContext<T>
- The context to providevalue: T
- The value to providefn: () -> any
- Function to execute with context
Returns: any
- Result of the function
Example:
local function App()
local theme = Rex.useState({
primary = Color3.fromRGB(70, 130, 255),
background = Color3.fromRGB(30, 30, 40)
})
return Rex.withContext(ThemeContext, theme:get(), function()
return MainUI()
end)
end
Utility Functions
Rex.isState(value: any): boolean
Checks if a value is a valid RexState object.
Parameters:
value: any
- Value to check
Returns: boolean
- True if value is a state object
Example:
local count = Rex.useState(0)
local number = 42
print(Rex.isState(count)) -- true
print(Rex.isState(number)) -- false
Type Definitions
export type RexState<T> = {
get: (self: RexState<T>) -> T,
set: (self: RexState<T>, value: T) -> (),
onChange: (self: RexState<T>, callback: (newValue: T, oldValue: T) -> ()) -> () -> (),
map: (self: RexState<T>, mapFn: (value: T) -> any) -> any,
update: (self: RexState<T>, updateFn: (current: T) -> T) -> (),
destroy: ((self: RexState<T>) -> ())?, -- For computed states
}
export type AsyncState<T> = {
data: RexState<T?>,
loading: RexState<boolean>,
error: RexState<string?>,
reload: () -> (),
}
export type RexContext<T> = {
defaultValue: T,
_contextId: string,
}