EZui is not battle tested, just yet.
ezui.useEffecthas been deprecated and replaced with a better, more granular system More Info
Tired of UI libraries that have unnecessary amounts of abstractions and hold your hand too much? Try EZui. EZui has near-identical performance to creating the functions for reactive UI yourself (most of the overhead is memory allocation).
So how does it work?
Most UI frameworks create a virtual tree which is then rendered directly by the engine, this approach ensures that the tree remains pure in states, however, this approach is unnecessarily expensive for Roblox, so instead, we just scrap it entirely.
Instead, EZui is assuming you’re creating UIs with Instance.new or :Clone(), so we provide the functions for binding our source objects directly to them (as well as a few utility functions for Children and Tags). This library does not hold your hand, so its possible to create dangerous code. Since we’re not doing expensive table iteration or dynamic accesses*, it means EZui dwarfs every framework currently available.
* we use dynamic allocation for effects, which is slower than we want, but theres not much we can do here
Example Usage
Below is a simple example that updates a counter TextButton when you click it, notice how we use an Instance directly instead of expensively creating new instances.
This assumes there a TextButton has the same parent as the script
local ezui = require(ReplicatedStorage.ezui)
local clickGetter, clickSetter = ezui.source(0)
ezui.useValue(script.Parent.TextLabel, "Text", function()
return `times clicked: {clickGetter()}`
end)
ezui.connectEvent(script.Parent.TextLabel, "Activated", function(senderParams, io, timesClicked)
clickSetter(timesClicked)
end)
Sources and Effects
The core system for our reactive UI is the Source and Effect system, this is inherited from Roblox/Signals, but made a lot simpler.
You can create a source using ezui.source, any value can be pushed into a Source
local getter, setter = ezui.source(0)
The getter function retrieves the captured value, while setter updates it. Sources are lazy-evaluated for performance.
Effects are events that are called when you call a source setter.
local getter, setter = ezui.source(0)
local dispose = ezui.effect(function()
print(getter())
end)
setter(2)
It’s important to keep a reference to
dispose, otherwise you’ll create a memory leak as effects are strongly referenced internally.
You can also put getters inside functions to create computed sources.
local function getName()
return firstNameGetter() .. " " .. lastNameGetter()
end
This will correctly wire to effects if you call it, as the getters are still called.
Using getters in EZui
You can bind a getter (or a function that contains getters) by using an associated effect binding function.
Effects can control properties, events, change events, tags and children. The passed getter should return the associated value for it to be connected to the instance.
local counterGetter, counterSetter = ezui.source(tick())
local dispose = ezui.useValue(TextLabel, "Text", function()
return `clicked {counterSetter} time(s)`
end)
ezui.connectEvent(TextLabel, "Activated", function(senderParams, io, timesClicked)
counterSetter(timesClicked)
end)
Like effects, it is important to keep a reference to
dispose, calling this disposer will NOT disconnect events bound through the effect, which is intentional, set the source value to nil first to disconnect it.
All events and effects are automatically disposed when an Instance is destroyed. This means when the Instance fires Instance.Destroying (which is not fired by some internal destruction events.)
Utility functions
As well as effects, EZui provides utility functions to make it easier to program some parts of the UI. These are provided below.
ezui.connectEvent
| Param | Type | Description |
|---|---|---|
inst |
Instance |
The instance to connect to |
eventName |
string |
The event name to connect to |
fn |
(SenderParams, ...any) -> () |
The event function |
Returns: dispose - A function to disconnect the event.
Connects an event on the Instance, and provides SenderParams, as well as the params of the event.
ezui.connectChangeSignal
| Param | Type | Description |
|---|---|---|
inst |
Instance |
The instance to connect to |
propName |
string |
The prop name to listen for changes on |
fn |
(SenderParams, propVal: any, propName: string) -> () |
The event function |
Returns: dispose - A function to disconnect the event.
Connects a change event to the instance, passes the changed value and prop name, as well as the SenderParams struct.
ezui.setChildren
| Param | Type | Description |
|---|---|---|
inst |
Instance |
The instance to update |
children |
{Instance} |
The new children |
Updates Children on an instance, only updates changed values. Removed instances are parented to nil and not destroyed.
ezui.setTags
| Param | Type | Description |
|---|---|---|
inst |
Instance |
The instance to update |
tags |
{string} |
The new tags |
Updates CollectionService tags on an instance, only updats changed values.
SenderParams
Events send a struct called SenderParams, which contains the sender and a function to disconnect the event.