A view management library for Roblox Lua similar to React
Full Changelog: https://github.com/Roblox/roact/compare/v1.4.3...v1.4.4
A number of small changes to error messaging and performance, as well as a forwardRef
API that matches React.
createReconciler
(#297).This release introduces the new Context API
Roact.createContext
This release fixes a major bug with fragments and a minor behavioral inconsistency with setState
.
setState
(#232)This release fixes a minor reconciliation bug and introduces a new binding API, Roact.joinBindings
.
nil
children caused the old children to not be unmounted. (#210)Roact.joinBindings
, which allows combining multiple bindings into a single binding that can be mapped. (#208)The new joinBindings
API can be used to combine multiple bindings and use them to create a new binding!
You can use this to implement flexible sizing without going through the reconciler, for example:
local function Flex()
local aSize, setASize = Roact.createBinding(Vector2.new())
local bSize, setBSize = Roact.createBinding(Vector2.new())
return Roact.createElement("Frame", {
Size = Roact.joinBindings({aSize, bSize}):map(function(sizes)
local sum = Vector2.new()
for _, size in ipairs(sizes) do
sum = sum + size
end
return UDim2.new(0, sum.X, 0, sum.Y)
end),
}, {
A = Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 0, 30),
[Roact.Change.AbsoluteSize] = function(instance)
setASize(instance.Size)
end,
}),
B = Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 0, 30),
Position = aSize:map(function(size)
return UDim2.new(0, 0, 0, size.Y)
end),
[Roact.Change.AbsoluteSize] = function(instance)
setBSize(instance.Size)
end,
}),
})
end
This release is the culmination of almost one year of work improving Roact's internals to pave the way for new features, optimizations, and ergonomic improvements.
We encourage existing projects to upgrade to 1.0.0 when possible. We feel that the improvements it brings over 0.2.0 and previous releases are substantial!
propTypes
.Component:setState
to be deferred if it's called while Roact is updating a component. (#183)Roact.Event
and Roact.Change
triggered by a Roact update to be deferred until Roact is done updating the instance.The Roact.createFragment
API enables returning multiple components from the render function of a function or stateful component.
Where we would previously have to create dummy Folder
instances or nest elements endlessly, we can now return a fragment containing multiple objects!
Before, in 0.2.0:
local function Connectors()
return Roact.createElement("Folder", {
Keyboard = Roact.createElement(KeyboardConnector),
Mouse = Roact.createElement(MouseConnector),
})
end
Now, in 1.0.0:
local function Connectors()
return Roact.createFragment({
Keyboard = Roact.createElement(KeyboardConnector),
Mouse = Roact.createElement(MouseConnector),
})
end
Bindings are a great way to build animations, as well as a good way to improve performance in cases where Roact isn't quite keeping up.
Check out our brand new bindings and refs guide for an introduction to how bindings can be used.
Bindings can be used to create an efficient, safe, and performant Roact fit-to-contents implementation:
local FitList = Roact.Component:extend("FitList")
function FitList:init()
self.size, self.setSize = Roact.createBinding(Vector2.new())
end
function FitList:render()
local children = {
Layout = Roact.createElement("UIListLayout", {
[Roact.Change.AbsoluteContentSize] = function(instance)
self.setSize(instance.AbsoluteContentSize)
end,
})
}
for key, child in pairs(self.props[Roact.Children]) do
children[key] = child
end
return Roact.createElement("Frame", {
Size = self.size:map(function(absoluteSize)
return UDim2.new(0, absoluteSize.X, 0, absoluteSize.Y)
end),
}, children)
end
Set the validateProps
value on your stateful component class to enable opting into strong type checking every time props update.
Prop validation is opt-in! To enable it in your project, use Roact.setGlobalConfig
once somewhere in your project:
Roact.setGlobalConfig({
propValidation = true,
})
When paired with libraries like t by Osyris, it's easy to make sure your components are accepting the correct props:
local CoolTextLabel = Roact.Component:extend("CoolTextLabel")
CoolTextLabel.validateProps = t.strictInterface({
text = t.string,
layoutOrder = t.optional(t.integer),
})
function CoolTextLabel:render()
return Roact.createElement("TextLabel", {
Size = UDim2.new(0, 400, 0, 400),
Text = self.props.text,
LayoutOrder = self.props.layoutOrder,
})
end
We have a huge new suite of documentation and error messages that should make everything more clear when you run into problems.
This is the first non-prerelease release of Roact, and the last one before we merge in a large refactor from our current development branch, new-reconciler
.
We consider this release to be a stable release for existing projects using Roact prereleases.
Later today, we'll be publishing Roact 1.0, which includes almost a year of significant refactoring work and introduces a substantial number of features. We wanted to mark this release as the last version using the old Roact internals that have been proven stable in projects like the Roblox iOS, Android, and Xbox applications.
Roact.reconcile
in favor of Roact.update
(#194)Roact.getGlobalConfigValue
, which let users read the current internal configuration.Roact.Element
, which let users figure out whether something is a Roact element. We'll introduce a proper type-checking API at a later date.This prerelease is a roll-up of the changes since the latest prerelease, which was awhile ago!
Since the last prerelease, we've been working out of a new branch that we've been grooming to become Roact 1.0. It's on the new-reconciler
branch if you're interested in checking it out, and it'll be merged to master within the next month or so hopefully!