### Legacy ReactDOM.render Example Source: https://github.com/roblox/react-luau/blob/main/modules/react-roblox/README.md This is the most recognizable React entry-point, though it is being phased out in favor of the 'root' APIs. ```javascript ReactDOM.render(

Hello, world!

, document.getElementById('root') ); ``` -------------------------------- ### Example: Convert Specific File to Luau Source: https://github.com/roblox/react-luau/blob/main/docs/align-files-guide.md An example demonstrating the file-by-file conversion command, targeting 'utils.js' within the react-devtools-shared module. Run this from the 'js-to-lua' directory. ```shell dist/apps/convert-js-to-lua/index.js \ --input ../react/packages/react-devtools-shared/src/utils.js \ --output ../roact-alignment/modules/ \ --rootDir ../react/packages \ --plugin=knownImports --plugin=jestGlobals \ --babelTransformConfig babel-flow-transform-react.config.json \ --babelConfig babel-flow.config.json ``` -------------------------------- ### React Memoization Example Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Shows how to use `React.memo` to create a memoized component for performance optimization. Guidance from React JS documentation applies. ```lua local MyComponent = React.memo(function MyComponent(props) --[[ render using props ]] end) ``` -------------------------------- ### React Create Element Example Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Illustrates the direct usage of `React.createElement` for creating UI elements in Roact, as JSX is not supported in Luau tooling. This example creates a frame with a child text label. ```lua --[[ React.createElement( type, [props], [...children] )]] local element = React.createElement( "Frame", { Size = UDim2.fromScale(1, 1) }, React.createElement("TextLabel", { Text = "Child1" }) React.createElement("TextLabel", { Text = "Child2" }) ) ``` -------------------------------- ### RoactCompat Migration Example Source: https://context7.com/roblox/react-luau/llms.txt Demonstrates how to use `RoactCompat` to maintain the legacy Roact API surface during migration to React 17+. Only the `require` path needs to change to use the compatibility layer. ```lua -- Before migration (legacy Roact): local Roact = require(Packages.Roact) local tree = Roact.mount(Roact.createElement("TextLabel", { Text = "Hi" }), parent, "UI") Roact.update(tree, Roact.createElement("TextLabel", { Text = "Updated" })) Roact.unmount(tree) -- After (RoactCompat — same API, new renderer): local Roact = require(Packages.RoactCompat) -- only this line changes local tree = Roact.mount(Roact.createElement("TextLabel", { Text = "Hi" }), parent, "UI") Roact.update(tree, Roact.createElement("TextLabel", { Text = "Updated" })) Roact.unmount(tree) -- RoactCompat.Ref and RoactCompat.Children map to the string keys used in Roact 17 local ref = Roact.createRef() Roact.createElement("TextBox", { [Roact.Ref] = ref, -- equivalent to ref = ref in Roact 17 [Roact.Children] = {}, -- equivalent to children = {} in Roact 17 }) -- Access new React APIs directly while using RoactCompat for old code local React = require(Packages.React) local function NewComponent() local value, setValue = React.useState(0) return React.createElement("TextLabel", { Text = tostring(value) }) end -- Mount via legacy API Roact.mount(Roact.createElement(NewComponent), parent) ``` -------------------------------- ### React Component Example Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Demonstrates how to define a React component using `Component:extend` and `init` method for state initialization. It uses `setState` to toggle component state and update the UI. ```lua local MyComponent = React.Component:extend("MyComponent") function MyComponent:init() sself:setState({ expanded = true }) end function MyComponent:render() return React.createElement("TextButton", { Text = if expanded then self.props.text else "Click to Expand", Size = if expanded then UDim2.new(1, 0, 0, 200) else UDim2.new(1, 0, 0, 30), [React.Event.Activated] = function() self:setState(function(expanded) return { expanded = not expanded } end) end }) end ``` -------------------------------- ### React.useMemo Example Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Caches a computed value between re-renders. Use this to avoid expensive calculations on every render if the dependencies have not changed. ```lua local function filterByCategory(todos, category) -- ... return filteredTodos end local function FilteredTodoList(props) local filteredTodos = React.useMemo(function() return filterByCategory(props.todos, props.selectedCategory) end, { props.todos, props.selectedCategory }) return React.createElement(TodoList, { items = filteredTodos, }) end ``` -------------------------------- ### Render a Simple Message Component in React Luau Source: https://github.com/roblox/react-luau/blob/main/README.md This example demonstrates how to create a basic React Luau component that renders a 'Hello' message. It requires importing React and ReactRoblox, and uses `createElement` to define UI elements. The component is then rendered into the player's GUI. ```luau local React = require(Packages.React) local ReactRoblox = require(Packages.ReactRoblox) local e = React.createElement local function HelloMessage(props: { name: string, }) return e("TextLabel", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), AutomaticSize = Enum.AutomaticSize.XY, Text = `Hello, {props.name}!`, }) end local function App() return e("ScreenGui", {}, { MyMessage = e(HelloMessage, { name = "Taylor", }), }) end local root = ReactRoblox.createRoot(Instance.new("Folder")) root:render(ReactRoblox.createPortal(e(App), Players.LocalPlayer.PlayerGui)) ``` -------------------------------- ### React.useCallback Example Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Caches a function definition to prevent unnecessary re-renders. Use this when passing callbacks to optimized child components that rely on reference equality. ```lua local ItemTile = require(...) local equipItem = require(...) local function EquippableItemTile(props) local onClick = React.useCallback(function() equipItem(props.itemId) end, { props.itemId }) return React.createElement(ItemTile, { text = props.itemName, onTileClicked = onClick, }) end ``` -------------------------------- ### TooltipButton Component Example Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/adopt-new-features.md Defines a `TooltipButton` component that displays a tooltip when clicked. It uses `useState` to manage the tooltip's visibility and `task.delay` to control its fade-out. ```lua local function TooltipButton(props) local showTooltip, setShowTooltip = React.useState(false) return React.createElement("Frame", { key = "TooltipButton", Size = UDim2.fromScale(1, 1), }, { Button = React.createElement("TextButton", { Text = "Show tooltip", Active = props.enabled, [React.Event.Activated] = function() setShowTooltip(true) task.delay(props.tooltipFadeDelay, function() setShowTooltip(false) end) end }) Tooltip = if showTooltip then React.createElement("TextLabel", { Text = "Tooltip text!", }) else nil }) end ``` -------------------------------- ### Managing State with Bindings using useBinding Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Use useBinding to create a reactive Binding object and its updater function, similar to useState. This example displays the absolute size of an ImageLabel and updates a TextLabel accordingly. ```lua local function DisplaysSize(props) local absSize, setAbsSize = React.useBinding(Vector2.new(0, 0)) return React.createElement(React.Fragment, nil, React.createElement("ImageLabel", { Image = props.image, [React.Change.AbsoluteSize] = function(rbx) setAbsSize(rbx.AbsoluteSize) end, }), React.createElement("TextLabel", { Text = absSize:map(function(value) return "X = " .. tostring(value.X) .. "; Y = " .. tostring(value.Y) end) } ) end ``` -------------------------------- ### Basic Component with Refs in ReactRoblox Source: https://github.com/roblox/react-luau/blob/main/modules/react-roblox/README.md A basic Roact component demonstrating the use of refs for UI elements. This example highlights a potential issue with property assignment order when refs are not backed by binding logic. ```lua local PopupButtons = Roact.Component:extend("PopupButtons") function PopupButtons:init() sself.confirmRef = Roact.createRef() sself.cancelRef = Roact.createRef() end function PopupButtons:render() --[[ "Some Description" [ Confirm ] [ Cancel ] ]] return Roact.createElement("Frame", nil { ConfirmButton = Roact.createElement("TextButton", { [Roact.Ref] = self.confirmRef, Text = "Confirm", NextSelectionRight = self.cancelRef.value, }), CancelButton = Roact.createElement("TextButton", { [Roact.Ref] = self.cancelRef, Text = "Confirm", NextSelectionLeft = self.confirmRef.value, }), }) end ``` -------------------------------- ### Assigning Instance References with Standard Refs Source: https://github.com/roblox/react-luau/blob/main/docs/deviations.md This example demonstrates a common issue when assigning Instance references to properties like NextSelectionRight and NextSelectionLeft. Due to the arbitrary render order of children, refs might be nil when accessed, leading to incorrect assignments. ```lua local PopupButtons = Roact.Component:extend("PopupButtons") function PopupButtons:init() sself.confirmRef = Roact.createRef() sself.cancelRef = Roact.createRef() end function PopupButtons:render() --[[ "Some Description" [ Confirm ] [ Cancel ] ]] return Roact.createElement("Frame", nil { ConfirmButton = Roact.createElement("TextButton", { [Roact.Ref] = self.confirmRef, Text = "Confirm", NextSelectionRight = self.cancelRef.value, }), CancelButton = Roact.createElement("TextButton", { [Roact.Ref] = self.cancelRef, Text = "Confirm", NextSelectionLeft = self.confirmRef.value, }), }) end ``` -------------------------------- ### Profiling Setup for react-reconciler Source: https://github.com/roblox/react-luau/blob/main/modules/react-reconciler/README.md This code demonstrates how to plug in a performance marking function to the global performance object when enabling the Scheduling Profiler. Ensure the `enableProfiling` flag is set in `SchedulerFeatureFlags.js` and `enableSchedulingProfiler` in `ReactFeatureFlags.js`. ```lua _G.performance = { mark = function(str) debug.profileBegin(str) debug.profileEnd(str) } ``` -------------------------------- ### Creating Bindings with React.createBinding for Class Components Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Explains how to use React.createBinding to create binding objects, similar to useBinding, for use within class components. This example shows creating a binding for Vector2 size and mapping it to a TextLabel. ```lua local DisplaysSize = React.Component:extend("DisplaysSize") function DisplaysSize:init() self.absSize, self.setAbsSize = React.createBinding(Vector2.new(0, 0)) end function DisplaysSize:render() return React.createElement(React.Fragment, nil, React.createElement("ImageLabel", { Image = self.props.image, [React.Change.AbsoluteSize] = function(rbx) self.setAbsSize(rbx.AbsoluteSize) end, }), React.createElement("TextLabel", { Text = self.absSize:map(function(value) return "X = " .. tostring(value.X) .. "; Y = " .. tostring(value.Y) end) } ) end ``` -------------------------------- ### Using Refs as Bindings for Instance References Source: https://github.com/roblox/react-luau/blob/main/docs/deviations.md This revised example shows how to solve the race condition by passing the ref itself (as a binding) instead of its value. This ensures that properties are correctly updated as refs are assigned, regardless of render order. ```lua -- ... return Roact.createElement("Frame", nil { ConfirmButton = Roact.createElement("TextButton", { [Roact.Ref] = self.confirmRef, Text = "Confirm", -- pass the ref itself, which is a binding NextSelectionRight = self.cancelRef, }), CancelButton = Roact.createElement("TextButton", { [Roact.Ref] = self.cancelRef, Text = "Confirm", -- pass the ref itself, which is a binding NextSelectionLeft = self.confirmRef, }), }) -- ... ``` -------------------------------- ### React.useReducer Example Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Manages complex state logic using a reducer function. Use this hook when state transitions are complex and involve multiple sub-values or when the next state depends on the previous one. ```lua local function reducer(state, action) if action.type == "add" then return { value = state.value + action.value, ops = state.ops + 1, } elseif action.type == "multiply" then return { value = state.value * action.value, ops = state.ops + 1, } end end local function Calculator(props) local state, dispatch = React.useReducer(reducer, { value = 1, ops = 0 }) return React.createElement(React.Fragment, nil, React.createElement("TextLabel", { Text = string.format("value: %d - ops: %d", state.value, state.ops) }) React.createElement("TextButton", { Text = "Add 4", [React.Event.Activated] = function() dispatch({ type = "add", value = 4 }) end }) React.createElement("TextButton", { Text = "Multiply by 3", [React.Event.Activated] = function() dispatch({ type = "multiply", value = 3 }) end }) ) end ``` -------------------------------- ### Create React Luau Elements with React.createElement Source: https://context7.com/roblox/react-luau/llms.txt Use React.createElement to define UI elements, as Luau lacks JSX. It accepts a type (string for host instances or a component), props, and children. This example shows creating host elements and rendering a function component. ```lua local React = require(Packages.React) local ReactRoblox = require(Packages.ReactRoblox) local e = React.createElement -- Host element with props and children local frame = e("Frame", { Size = UDim2.fromScale(1, 1) }, e("TextLabel", { Text = "Hello", LayoutOrder = 1 }), e("TextLabel", { Text = "World", LayoutOrder = 2 }) ) -- Function component local function Greeting(props: { name: string }) return e("TextLabel", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), AutomaticSize = Enum.AutomaticSize.XY, Text = `Hello, {props.name}!`, }) end local root = ReactRoblox.createRoot(Instance.new("Folder")) root:render(ReactRoblox.createPortal( e(Greeting, { name = "Taylor" }), Players.LocalPlayer.PlayerGui )) ``` -------------------------------- ### Legacy Roact Mounting, Updating, and Unmounting Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/convert-legacy-conventions.md Demonstrates the traditional Roact lifecycle management using Roact.mount, Roact.update, and Roact.unmount. ```lua local PlayerGui = game:GetService("Players").LocalPlayer.PlayerGui local Roact = require(Packages.Roact) local roactTree = Roact.mount(Roact.createElement("TextLabel", { Text = "Hello world!", }, PlayerGui) task.wait(3) roactTree = Roact.update(roactTree, Roact.createElement("TextLabel", { Text = "Hello Roblox!", }) task.wait(3) Roact.unmount(roactTree) ``` -------------------------------- ### React.useRef Example Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Stores a mutable value that does not trigger re-renders when changed. Useful for accessing Roblox Instances or holding any mutable data. ```lua local function TextBoxWithButton(props) local textBoxRef = React.useRef(nil) return React.createElement(React.Fragment, nil, React.createElement("TextBox", { ref = textBoxRef }), React.createElement("TextButton", { Text = "->", [React.Event.Activated] = function() textBoxRef.current:CaptureFocus() end }), ) end ``` -------------------------------- ### Count Children with React.Children.count Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Get the total number of child members in props.children using React.Children.count. Note that userdata children are not included in the count. ```lua local function List(props) print(string.format("Render list with %d children", React.Children.count(props.children))) return React.createElement( "Frame", { Size = UDim2.fromScale(1, 1) }, props.children, ) end ``` -------------------------------- ### RoactCompat.Ref Usage Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Demonstrates how RoactCompat.Ref allows legacy Roact prop tables to be equivalent to Roact 17 prop tables using the 'ref' prop. ```lua Roact.createElement("TextLabel", { Text = "Hello", [Roact.Ref] = textLabelRef, }) ``` ```lua Roact.createElement("TextLabel", { Text = "Hello", ref = textLabelRef, }) ``` -------------------------------- ### connectToDevtools Source: https://github.com/roblox/react-luau/blob/main/modules/react-devtools-core/README.md Sets up a connection to DevTools. This function should be run in the same context as React and before importing any React packages. ```APIDOC ## connectToDevtools(config) ### Description Connects to the React DevTools. ### Method `connectToDevtools` ### Parameters #### Config Object - **host** (string) - Optional - The host to connect to. Defaults to "localhost". - **port** (number) - Optional - The port to connect to. Defaults to `8097`. - **useHttps** (boolean) - Optional - Whether to use a secure WebSocket protocol (wss). Defaults to `false`. - **resolveRNStyle** (`(style: number) => ?Object`) - Optional - A function used by the React Native style plug-in. - **isAppActive** (`() => boolean`) - Optional - A function that DevTools will poll. Connection will wait until this function returns `true`. ``` -------------------------------- ### Marking ROBLOX Deviations Source: https://github.com/roblox/react-luau/blob/main/docs/align-files-guide.md Example of how to mark code deviations in Luau. Uncomment the original line, wrap it with deviation comments, and provide the corrected implementation. ```lua -- ROBLOX deviation START: -- Boolean.toJSBoolean(value) -- ROBLOX deviation END value ``` -------------------------------- ### Roact 17 Mounting, Updating, and Unmounting with createRoot Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/convert-legacy-conventions.md Shows the Roact 17 approach using ReactRoblox.createRoot for managing UI lifecycle, including rendering and unmounting via root:render(nil). ```lua local PlayerGui = game:GetService("Players").LocalPlayer.PlayerGui local React = require(Packages.React) local ReactRoblox = require(Packages.ReactRoblox) -- Roact 17 roots will take full ownership of the instance provided to them, -- so we should not create a root using PlayerGui directly local container = Instance.new("Folder") container.Parent = PlayerGui local root = ReactRoblox.createRoot(container) root:render(Roact.createElement("TextLabel", { Text = "Hello world!", }) task.wait(3) root:render(Roact.createElement("TextLabel", { Text = "Hello Roblox!", }) task.wait(3) root:render(nil) ``` -------------------------------- ### Mapping Binding Value for Display Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Use the `map` method on a binding to transform its value for display or use in other properties. This example formats a click count into a user-friendly string. ```lua local function UpdateOnClick() local count, setCount = React.useBinding(0) return React.createElement("TextButton", { Text = count:map(function(value) return string.format("Clicked %d times", value) end), [React.Event.Activated] = function() setCount(count:getValue() + 1) end, }) end ``` -------------------------------- ### RoactCompat.mount Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Compatibility method mimicking legacy Roact.mount. It creates a root, renders the element, and returns a handle for updates and unmounting. ```APIDOC ## RoactCompat.mount ### Description Compatibility method mimicking [legacy `Roact.mount`](https://roblox.github.io/roact/api-reference/#roactmount). For all intents and purposes, this should function equivalently to legacy Roact's `mount` function. Under the hood, RoactCompat takes the following steps: 1. Creates a root using [`React.createRoot`](react.md#reactcreateroot) * When `_G.__ROACT_17_COMPAT_LEGACY_ROOT__` is enabled, this will use [`React.createLegacyRoot`](react.md#reactcreatelegacyroot) instead 2. Calls `root:render` with the provided element * React's roots take complete control of the provided container, deleting all existing children. Legacy Roact does not tamper with existing children of the provided container. To mimic the legacy behavior, we use a [`Portal`](react.md#reactcreateportal) to mount into the container instead of providing it directly to the root. * When `_G.__ROACT_17_INLINE_ACT__` is enabled, the `render` call is automatically wrapped in [`ReactRoblox.act`](react-roblox.md#reactrobloxact) to ensure that mounting behavior resolves synchronously in tests. 3. Returns an opaque handle to the root that can be used with [`RoactCompat.update`](#roactcompatupdate) and [`RoactCompat.unmount`](#roactcompatunmount) ### Method Signature ```lua RoactCompat.mount(element: ReactElement, container: Instance?, name: string?): RoactTree ``` ``` -------------------------------- ### Customizing Component Interface with useImperativeHandle Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Use useImperativeHandle to expose a custom interface from a component to its parent via a ref. This example shows how to expose a `scrollToTop` function to control a ScrollingFrame. ```lua local ControllableScrollingFrame = React.forwardRef(function(props, ref) local innerRef = React.useRef(nil) React.useImperativeHandle(ref, function() return { scrollToTop = function() innerRef.current.CanvasPosition = Vector2.new(0, 0) end } end, {}) return React.createElement("ScrollingFrame", { ref = innerRef -- ... }) end) -- Another component will be able to access this interface via a ref: local function TodoListApp(props) local scrollingFrameRef = React.useRef(nil) return React.createElement(ControllableScrollingFrame, { ref = scrollingFrameRef }, React.createElement(TodoList, { items = props.todoItems, }), React.createElement("TextButton", { Text = "Back to top", [React.Event.Activated] = function() scrollingFrameRef.current.scrollToTop() end }) ) end ``` -------------------------------- ### Legacy Implicit Ref Forwarding vs Explicit Ref Forwarding Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/minimum-requirements.md Demonstrates the legacy method of implicit ref forwarding using `Roact.Ref` and the Roact 17+ compatible explicit ref forwarding using `Roact.forwardRef`. ```lua local function FancyButton(props) return Roact.createElement("TextBox", { PlaceholderText = "Enter your text here", PlaceholderColor3 = Color3.new(0.4, 0.4, 0.4), [Roact.Change.Text] = props.onTextChange, -- Implicitly forwarding a ref via the `Roact.Ref` prop [Roact.Ref] = props[Roact.Ref], }) end ``` ```lua local FancyButton = Roact.forwardRef(function(props, ref) return Roact.createElement("TextBox", { PlaceholderText = "Enter your text here", PlaceholderColor3 = Color3.new(0.4, 0.4, 0.4), [Roact.Change.Text] = props.onTextChange, -- Explicitly forwarding a ref passed in via `forwardRef` [Roact.Ref] = ref, end) end) ``` -------------------------------- ### Roact Warning for Duplicate Key Methods Source: https://github.com/roblox/react-luau/blob/main/docs/deviations.md Roact issues a warning if both table keys and the 'key' prop are used simultaneously for the same component. This example demonstrates a scenario that triggers such a warning. ```lua return React.createElement("Frame", nil, { Label = React.createElement("TextLabel", { key = "label1", Text = "Hello", }) }) ``` -------------------------------- ### Require and Connect to DevTools Source: https://github.com/roblox/react-luau/blob/main/modules/react-devtools-core/README.md Require the package and call `connectToDevtools` before importing any React packages. This sets up the connection to DevTools with optional configuration. ```luau local ReactDevtoolsCore = require(Packages.ReactDevtoolsCore) local connectToDevtools = ReactDevtoolsCore.connectToDevtools connectToDevTools(config) ``` -------------------------------- ### Testing Tooltip Behavior with `act` Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/adopt-new-features.md Demonstrates how to use `ReactRoblox.act` to test component behavior, including rendering, state changes, and event handling. It ensures all scheduled work is flushed before assertions. ```lua local React = require(Packages.React) local ReactRoblox = require(Packages.ReactRoblox) local container, root beforeEach(function() container = Instance.new("ScreenGui") container.Parent = Players.LocalPlayer.PlayerGui root = ReactRoblox.createRoot(container) end) afterEach(function() container:Destroy() end) it("shows a tooltip on click and hides it after a delay", function() -- Use `act` for the initial render ReactRoblox.act(function() -- Render the button in a disabled state root:render(React.createElement(TooltipButton, { enabled = false, tooltipFadeDelay = 1, })) end) expect(container.TooltipButton.Tooltip).toBeNil() expect(container.TooltipButton.Button.Active).toBe(false) -- Use `act` to re-render the tree ReactRoblox.act(function() -- Rerender in the enabled state root:render(React.createElement(TooltipButton, { enabled = true, tooltipFadeDelay = 1, })) end) expect(container.TooltipButton.Tooltip).toBeNil() expect(container.TooltipButton.Button.Active).toBe(true) -- Use `act` to trigger virtual input local element = Rhodium.Element.new(container.TooltipButton.Button) ReactRoblox.act(function() -- Click the button to trigger the tooltip element:click() Rhodium.VirtualInput.waitForInputEventsProcessed() end) expect(container.TooltipButton.Tooltip).never.toBeNil() expect(container.TooltipButton.Tooltip.Text).toEqual("Tooltip text!") -- Use `act` to resume queued renders after a delayed callback fires ReactRoblox.act(function() task.wait(1) end) expect(container.TooltipButton.Tooltip).toBeNil() end) ``` -------------------------------- ### Update Legacy Component Lifecycle Names Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/convert-legacy-conventions.md Update legacy lifecycle method names to match React JS conventions for better ecosystem compatibility. For example, `didMount` should be renamed to `componentDidMount`. ```lua local FocusButton = Roact.Component:extend("FocusButton") function FocusButton:init() sself.ref = Roact.createRef() end function FocusButton:render() return Roact.createElement("Button", { Size = self.props.Size, [Roact.Ref] = self.ref }, self.props[Roact.Children]) end function FocusButton:didMount() GuiService.SelectedObject = self.ref.current end ``` ```lua local FocusButton = Roact.Component:extend("FocusButton") function FocusButton:init() sself.ref = Roact.createRef() end function FocusButton:render() return Roact.createElement("Button", { Size = self.props.Size, ref = self.ref }, self.props.children) end function FocusButton:didMount() GuiService.SelectedObject = self.ref.current end ``` -------------------------------- ### Updating Binding Value Relative to Current Value Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Use `getValue` to retrieve the current value of a binding, useful for updates relative to the current state. This example increments a counter when a button is activated. ```lua local function UpdateOnClick() local count, setCount = React.useBinding(0) return React.createElement("TextButton", { Text = count, [React.Event.Activated] = function() setCount(count:getValue() + 1) end, }) end ``` -------------------------------- ### Handling UI Events and Properties with React.Event, React.Change, and React.Tag Source: https://context7.com/roblox/react-luau/llms.txt Utilize special prop keys like `React.Event` for Instance events, `React.Change` for property change signals, and `React.Tag` for `CollectionService` tags. These are automatically managed on mount and unmount. ```lua local React = require(Packages.React) local function InteractiveFrame() local hovered, setHovered = React.useState(false) local scrollPos, setScrollPos = React.useState(Vector2.new(0, 0)) return React.createElement("Frame", { Size = UDim2.fromScale(1, 1), BackgroundColor3 = if hovered then Color3.fromRGB(200, 200, 255) else Color3.fromRGB(255, 255, 255), -- React.Event: connect to MouseEnter / MouseLeave events [React.Event.MouseEnter] = function() setHovered(true) end, [React.Event.MouseLeave] = function() setHovered(false) end, -- React.Tag: apply CollectionService tags on mount [React.Tag] = "interactive-frame ui-element", }, React.createElement("ScrollingFrame", { Size = UDim2.fromScale(1, 0.8), CanvasSize = UDim2.fromScale(1, 3), -- React.Change: subscribe to a property changed signal [React.Change.CanvasPosition] = function(rbx) setScrollPos(rbx.CanvasPosition) end, }), React.createElement("TextLabel", { Text = string.format("Scroll: %.0f, %.0f", scrollPos.X, scrollPos.Y), Size = UDim2.new(1, 0, 0, 30), }) ) end ``` -------------------------------- ### Legacy Context API vs Roact 17+ Context API Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/minimum-requirements.md Compares the legacy method of reading style from context with the Roact 17+ compatible method using `createContext` and `StyleContext.Provider`/`StyleContext.Consumer`. ```lua local AppStyle = require(script.Parent.AppStyle) local Label = Roact.Component:extend("Label") function Label:init() -- reading style from context self.style = self._context.style end function Label:render() return Roact.createElement("TextLabel", { BackgroundColor3 = self.style.LabelColor, Text = props.text, }) end local App = Roact.Component:extend("App") function App:init() -- defining style in context self._context.style = AppStyle end function App:render() return Roact.createElement("Frame", { Size = UDim2.fromScale(1, 1) }, { Start = Roact.createElement(Button, { text = "Hello World", }) }) end ``` ```lua local AppStyle = require(script.Parent.AppStyle) local StyleContext = Roact.createContext(nil) local Label = Roact.Component:extend("Label") function Label:render() return Roact.createElement(StyleContext.Consumer, { render = function(style) return Roact.createElement("TextLabel", { BackgroundColor3 = style.LabelColor, Text = props.text, end }) end end) end local App = Roact.Component:extend("App") function App:render() return Roact.createElement(StyleContext.Provider, { value = AppStyle, }, { App = Roact.createElement("Frame", { Size = UDim2.fromScale(1, 1) }, { Start = Roact.createElement(Button, { text = "Hello World", }) }) }) end ``` -------------------------------- ### Getting the Single Child from Children with RoactCompat Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md RoactCompat.oneChild retrieves a single child from a collection of children, providing functionality similar to React.Children.only but with added capability to unwrap a table containing a single element. ```luau RoactCompat.oneChild( children: { [string | number]: ReactElement } | ReactElement | nil ): ReactElement ``` -------------------------------- ### Optimizing with useCallback and useMemo Source: https://context7.com/roblox/react-luau/llms.txt Use `useMemo` to cache the result of expensive computations, recomputing only when dependencies change. Use `useCallback` to memoize function references, ensuring stable references for child components unless dependencies change. ```lua local React = require(Packages.React) local function expensiveFilter(items, category) -- simulate expensive computation local result = {} for _, item in items do if item.category == category then table.insert(result, item) end end return result end local function ItemList(props) -- useMemo: recompute filtered list only when inputs change local filteredItems = React.useMemo(function() return expensiveFilter(props.items, props.selectedCategory) end, { props.items, props.selectedCategory }) -- useCallback: stable handler reference for child components local handleSelect = React.useCallback(function(itemId) props.onSelectItem(itemId) end, { props.onSelectItem }) local children = {} for i, item in filteredItems do children[item.id] = React.createElement("TextButton", { Text = item.name, LayoutOrder = i, [React.Event.Activated] = function() handleSelect(item.id) end, }) end return React.createElement("Frame", { Size = UDim2.fromScale(1, 1) }, children) end ``` -------------------------------- ### Applying CollectionService Tags with React.Tag Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Demonstrates how to apply CollectionService tags to a host component using React.Tag. Multiple tags can be applied by providing a space-delimited string. Tags are applied when the component mounts. ```lua local button = Roact.createElement("TextButton", { [React.Tag] = "confirm-button" Text = "Confirm", -- ... }) ``` ```lua [React.Tag] = "some-tag some-other-tag" ``` -------------------------------- ### RoactCompat.Portal Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Compatibility component mimicking Roact.Portal. It uses React.createPortal under the hood. ```APIDOC ## RoactCompat.Portal ### Description Compatibility component mimicking [`Roact.Portal`](https://roblox.github.io/roact/api-reference/#roactportal). Uses the [React.createPortal](react.md#reactcreateportal) function under the hood. ### Usage ```lua local RoactCompat = require(path.to.RoactCompat) local MyPortal = RoactCompat.Portal -- Usage within a component: local element = { ...children... } ``` ``` -------------------------------- ### RoactCompat.Children Usage Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Shows how RoactCompat.Children enables legacy Roact prop tables to be equivalent to Roact 17 prop tables using the 'children' prop for forwarding children. ```lua -- forwards the children provided to this component Roact.createElement("Frame", nil, self.props[Roact.Children]) ``` ```lua -- forwards the children provided to this component Roact.createElement("Frame", nil, self.props.children) ``` -------------------------------- ### Combine Multiple Bindings with React.joinBindings Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Use React.joinBindings to merge several bindings into one. The resulting binding updates when any of the input bindings change. This example demonstrates combining two size bindings to dynamically set the size of a parent Frame. ```lua local function Flex() local aSize, setASize = React.useBinding(Vector2.new()) local bSize, setBSize = React.useBinding(Vector2.new()) return React.createElement( "Frame", { Size = React.joinBindings({aSize, bSize}):map(function(sizes) local sum = Vector2.new() for _, size in sizes do sum += size end return UDim2.new(0, sum.X, 0, sum.Y) end), }, React.createElement("Frame", { Size = UDim2.new(1, 0, 0, 30), [React.Change.AbsoluteSize] = function(instance) setASize(instance.Size) end, }), React.createElement("Frame", { Size = UDim2.new(1, 0, 0, 30), Position = aSize:map(function(size) return UDim2.new(0, 0, 0, size.Y) end), [React.Change.AbsoluteSize] = function(instance) setBSize(instance.Size) end, }) ) end ``` -------------------------------- ### Migrate Reserved 'key' Prop to 'order' in Roact Components Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/minimum-requirements.md When migrating to Roact 17+, avoid using 'key' as a prop name. This example shows how to replace 'key' with 'order' to prevent conflicts with Roact's internal key management. ```lua local function OptionButton(props) return Roact.createElement("TextButton", { LayoutOrder = props.key, Text = props.text, [Roact.Event.Activated] = props.onClick, }) end local function ButtonGroup(props) return Roact.createFragment({ CancelButton = Roact.createElement(OptionButton, { key = 1, text = "Cancel", onClick = props.cancelCallback, }) ConfirmButton = Roact.createElement(OptionButton, { key = 2, text = "Confirm", onClick = props.confirmCallback, }) }) end ``` ```lua local function OptionButton(props) return Roact.createElement("TextButton", { LayoutOrder = props.order, Text = props.text, [Roact.Event.Activated] = props.onClick, }) end local function ButtonGroup(props) return Roact.createFragment({ CancelButton = Roact.createElement(OptionButton, { order = 1, text = "Cancel", onClick = props.cancelCallback, }) ConfirmButton = Roact.createElement(OptionButton, { order = 2, text = "Confirm", onClick = props.confirmCallback, }) }) end ``` -------------------------------- ### Convert Legacy Roact Portal to Roact 17 Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/convert-legacy-conventions.md Roact 17 uses `ReactRoblox.createPortal` for creating portals, differing from the legacy Roact syntax which used `Roact.Portal` with a target prop. ```lua local PlayerGui = game:GetService("Players").LocalPlayer.PlayerGui local function Modal(props) return Roact.createElement(Roact.Portal, { target = PlayerGui, }, { Modal = Roact.createElement("ScreenGui", {}, { Label = Roact.createElement("TextButton", { Text = "Click me to close!", [Roact.Event.Activated] = props.onClose, }) }) }) end ``` ```lua local PlayerGui = game:GetService("Players").LocalPlayer.PlayerGui local function Modal(props) return ReactRoblox.createPortal({ Modal = Roact.createElement("ScreenGui", {}, { Label = Roact.createElement("TextButton", { Text = "Click me to close!", [Roact.Event.Activated] = props.onClose, }) }) }, PlayerGui) end ``` -------------------------------- ### Conditionally Swap Roact API at Runtime Source: https://github.com/roblox/react-luau/blob/main/docs/migrating-from-1x/upgrading-to-roact-17.md In your entry point script, check a global flag to determine whether to overwrite the Roact API with RoactCompat. This allows testing with Roact 17 before full adoption. ```lua if _G.__NEW_ROACT__ then local Roact = require(Packages.Roact) local RoactCompat = require(Packages.Dev.RoactCompat) -- Overwrite the contents of the `Roact` package with that of the -- RoactCompat package for api, _ in Roact do Roact[api] = RoactCompat[api] end end -- Proceed with test setup... ``` -------------------------------- ### Mounting a Roact Element with RoactCompat Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Use RoactCompat.mount to render a ReactElement into a container, mimicking legacy Roact behavior. It creates a root, renders the element, and returns a handle for updates and unmounting. Note that React's roots take full control of the container, so a Portal is used to preserve existing children. ```luau RoactCompat.mount( element: ReactElement, container: Instance?, name: string? ): RoactTree ``` -------------------------------- ### Roact 'init' Lifecycle Method Source: https://github.com/roblox/react-luau/blob/main/docs/deviations.md The 'init' method serves as the constructor for Roact class components, running immediately after instance creation. It replaces the need for `super` calls found in other languages. ```lua local MyComponent = React.Component:extend("MyComponent") function MyComponent:init(props) sself.state = { value = 0 } end function MyComponent:render() return React.createElement("TextLabel", {Text = self.props.text}) end ``` -------------------------------- ### Animating Transparency with React.useBinding Source: https://context7.com/roblox/react-luau/llms.txt Use `useBinding` to directly update Instance properties for high-frequency changes like animations without triggering re-renders. Ensure `RunService.Heartbeat` is connected for continuous updates. ```lua local React = require(Packages.React) -- useBinding: animate a transparency value without triggering re-renders local function AnimatedLabel(props) local transparency, setTransparency = React.useBinding(0) React.useEffect(function() -- Drive transparency directly via binding update local connection = RunService.Heartbeat:Connect(function(dt) setTransparency((os.clock() % 2) / 2) -- pulses 0–1 end) return function() connection:Disconnect() end end, {}) return React.createElement("TextLabel", { Text = props.text, -- Binding applied directly; no re-render needed BackgroundTransparency = transparency, Size = UDim2.fromScale(1, 0.1), }) end ``` -------------------------------- ### ReactRoblox.createLegacyRoot Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react-roblox.md Creates a legacy root, similar to pre-concurrent React behavior. Refer to ReactDOM's documentation for details. ```APIDOC ## ReactRoblox.createLegacyRoot Adopted as part of the Concurrent Mode API. "Legacy" roots are essentially equivalent to pre-concurrent React behavior. Refer to [`ReactDOM.createLegacyRoot` documentation](https://reactjs.org/docs/concurrent-mode-adoption.html#migration-step-blocking-mode). ``` -------------------------------- ### RoactCompat.unmount Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Compatibility method mimicking legacy Roact.unmount. It unmounts a Roact tree from its container. ```APIDOC ## RoactCompat.unmount ### Description Compatibility method mimicking [legacy `Roact.unmount`](https://roblox.github.io/roact/api-reference/#roactunmount). ### Method Signature ```lua RoactCompat.unmount(tree: RoactTreeHandle) ``` ``` -------------------------------- ### ReactRoblox.createRoot Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react-roblox.md Creates a root for concurrent mode rendering. Refer to ReactDOM's documentation for detailed usage. ```APIDOC ## ReactRoblox.createRoot Adopted as part of the Concurrent Mode API. Refer to [`ReactDOM.createRoot` documentation](https://reactjs.org/docs/concurrent-mode-reference.html#createroot). ``` -------------------------------- ### RoactCompat.createFragment Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Compatibility method mimicking Roact.createFragment. It creates a fragment element. ```APIDOC ## RoactCompat.createFragment ### Description Compatibility method mimicking [`Roact.createFragment`](https://roblox.github.io/roact/api-reference/#roactcreatefragment). Uses the special component [`React.Fragment`](react.md#reactfragment) under the hood. ### Method Signature ```lua RoactCompat.createFragment(elements: { [string | number]: ReactElement }): ReactElement ``` ``` -------------------------------- ### React.Component Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/react.md Documentation for creating class components in React for Roblox. It details differences from React JS, such as using `Component:extend` and an `init` method instead of a constructor, and state initialization via `setState`. ```APIDOC ## React.Component ### Description Luau does not have ES6's class semantics, so class components work slightly differently from React JS. Use `Component:extend` in place of ES6 class semantics and implement an `init` method on components instead of a constructor. State is initialized using `setState`. ### Example ```lua local MyComponent = React.Component:extend("MyComponent") function MyComponent:init() sself:setState({ expanded = true }) end function MyComponent:render() return React.createElement("TextButton", { Text = if expanded then self.props.text else "Click to Expand", Size = if expanded then UDim2.new(1, 0, 0, 200) else UDim2.new(1, 0, 0, 30), [React.Event.Activated] = function() self:setState(function(expanded) return { expanded = not expanded } end) end }) end ``` ``` -------------------------------- ### Updating a Roact Tree with RoactCompat Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Use RoactCompat.update to re-render an existing Roact tree with a new element. The 'tree' argument must be a handle obtained from a previous RoactCompat.mount or RoactCompat.update call. ```luau RoactCompat.update(tree: RoactTree, element: ReactElement): RoactTree ``` -------------------------------- ### Using setState in Roact 'init' Method Source: https://github.com/roblox/react-luau/blob/main/docs/deviations.md It is recommended to use `setState` within the 'init' method (constructor equivalent) in Roact 17+. This avoids direct assignment to `self.state` and ensures consistent state updates. ```lua local MyComponent = React.Component:extend("MyComponent") function MyComponent:init(props) sself.setState({ count = 0 }) end function MyComponent:render() return React.createElement("TextLabel", {Text = "Count: " .. self.state.count}) end ``` -------------------------------- ### RoactCompat.update Source: https://github.com/roblox/react-luau/blob/main/docs/api-reference/roact-compat.md Compatibility method mimicking legacy Roact.update. It updates an existing Roact tree with a new element. ```APIDOC ## RoactCompat.update ### Description Compatibility method mimicking [legacy `Roact.update`](https://roblox.github.io/roact/api-reference/#roactupdate). The first argument should be the value returned from a prior call to [`RoactCompat.mount`](#roactcompatmount) or `RoactCompat.update`. This function will not work if the argument passed in was created with legacy Roact. ### Method Signature ```lua RoactCompat.update(tree: RoactTree, element: ReactElement): RoactTree ``` ```