@@ -4,6 +4,8 @@ local useFinePatchBlock = TS.import(script, game:GetService("ReplicatedStorage")
44local _react = TS .import (script , game :GetService ("ReplicatedStorage" ), "rbxts_include" , "node_modules" , "@rbxts" , "react" )
55local React = _react
66local useState = _react .useState
7+ local useMemo = _react .useMemo
8+ local useVirtualList = TS .import (script , game :GetService ("ReplicatedStorage" ), "rbxts_include" , "node_modules" , "@decillion" , "runtime" , "out" ).useVirtualList
79--[[
810 *
911 * Benchmark: Massive List Rendering
@@ -17,9 +19,11 @@ local function TraditionalListItem(_param)
1719 local name = _param .name
1820 local value = _param .value
1921 local isActive = _param .isActive
22+ local layoutOrder = _param .layoutOrder
2023 return React .createElement ("frame" , {
2124 Size = UDim2 .new (1 , 0 , 0 , 40 ),
2225 BackgroundColor3 = if isActive then Color3 .fromRGB (100 , 150 , 100 ) else Color3 .fromRGB (80 , 80 , 80 ),
26+ LayoutOrder = layoutOrder ,
2327 }, React .createElement ("textlabel" , {
2428 Text = `{name }: {value }` ,
2529 Size = UDim2 .new (0.8 , 0 , 1 , 0 ),
@@ -39,10 +43,12 @@ local function OptimizedListItem(_param)
3943 local name = _param .name
4044 local value = _param .value
4145 local isActive = _param .isActive
42- return useFinePatchBlock (function (isActive , name , value , id )
46+ local layoutOrder = _param .layoutOrder
47+ return useFinePatchBlock (function (isActive , layoutOrder , name , value , id )
4348 return React .createElement ("frame" , {
4449 Size = UDim2 .new (1 , 0 , 0 , 40 ),
4550 BackgroundColor3 = if isActive then Color3 .fromRGB (100 , 150 , 100 ) else Color3 .fromRGB (80 , 80 , 80 ),
51+ LayoutOrder = layoutOrder ,
4652 }, React .createElement ("textlabel" , {
4753 Text = `{name }: {value }` ,
4854 Size = UDim2 .new (0.8 , 0 , 1 , 0 ),
@@ -55,13 +61,18 @@ local function OptimizedListItem(_param)
5561 TextColor3 = Color3 .fromRGB (200 , 200 , 200 ),
5662 BackgroundTransparency = 1 ,
5763 }))
58- end , { isActive , name , value , id }, { {
64+ end , { isActive , layoutOrder , name , value , id }, { {
5965 elementPath = {},
6066 edits = { {
6167 type = 8 ,
6268 dependencyKey = "isActive" ,
6369 propName = "BackgroundColor3" ,
6470 path = {},
71+ }, {
72+ type = 1 ,
73+ dependencyKey = "layoutOrder" ,
74+ propName = "LayoutOrder" ,
75+ path = {},
6576 } },
6677 }, {
6778 elementPath = { 0 , 0 },
@@ -79,36 +90,40 @@ local function OptimizedListItem(_param)
7990 propName = "Text" ,
8091 path = { 0 , 1 },
8192 } },
82- } }, "dynamic_frame_73bz0ezrr " )
93+ } }, "dynamic_frame_n9yr06b60 " )
8394end
8495local function MassiveListBenchmark ()
8596 local itemCount , setItemCount = useState (1000 )
8697 local useOptimized , setUseOptimized = useState (true )
8798 local updateTrigger , setUpdateTrigger = useState (0 )
8899 local renderTime , setRenderTime = useState (0 )
100+ local ITEM_HEIGHT = 45
89101 -- Generate test data
90- local items = {}
91- do
92- local i = 0
93- local _shouldIncrement = false
94- while true do
95- if _shouldIncrement then
96- i += 1
97- else
98- _shouldIncrement = true
99- end
100- if not (i < itemCount ) then
101- break
102+ local items = useMemo (function ()
103+ local generated = {}
104+ do
105+ local i = 0
106+ local _shouldIncrement = false
107+ while true do
108+ if _shouldIncrement then
109+ i += 1
110+ else
111+ _shouldIncrement = true
112+ end
113+ if not (i < itemCount ) then
114+ break
115+ end
116+ local _arg0 = {
117+ id = i ,
118+ name = `Item {i }` ,
119+ value = math.random (1 , 1000 ),
120+ isActive = math.random () > 0.7 ,
121+ }
122+ table.insert (generated , _arg0 )
102123 end
103- local _arg0 = {
104- id = i ,
105- name = `Item {i }` ,
106- value = math.random (1 , 1000 ),
107- isActive = math.random () > 0.7 ,
108- }
109- table.insert (items , _arg0 )
110124 end
111- end
125+ return generated
126+ end , { itemCount , updateTrigger })
112127 local triggerUpdate = function ()
113128 local startTime = tick ()
114129 setUpdateTrigger (updateTrigger + 1 )
@@ -119,7 +134,14 @@ local function MassiveListBenchmark()
119134 end )
120135 end
121136 local ListComponent = if useOptimized then OptimizedListItem else TraditionalListItem
122- return useFinePatchBlock (function (itemCount , useOptimized , triggerUpdate , renderTime , items , ListComponent )
137+ local virtualList = useVirtualList ({
138+ items = items ,
139+ itemHeight = ITEM_HEIGHT ,
140+ overscan = 6 ,
141+ })
142+ local baseLayoutOrder = virtualList .range .start * 2
143+ local visibleCount = # virtualList .visibleItems
144+ return useFinePatchBlock (function (itemCount , useOptimized , triggerUpdate , renderTime , baseLayoutOrder , virtualList , visibleCount )
123145 return React .createElement ("frame" , {
124146 Size = UDim2 .new (1 , 0 , 1 , 0 ),
125147 BackgroundColor3 = Color3 .fromRGB (20 , 20 , 20 ),
@@ -158,12 +180,19 @@ local function MassiveListBenchmark()
158180 Position = UDim2 .new (0.7 , 0 , 0 , 35 ),
159181 TextColor3 = Color3 .fromRGB (200 , 200 , 200 ),
160182 BackgroundTransparency = 1 ,
161- }), React .createElement ("textlabel " , {
183+ }), React .createElement ("textbutton " , {
162184 Text = `Items: {itemCount }` ,
163- Size = UDim2 .new (0.5 , 0 , 0 , 30 ),
185+ Size = UDim2 .new (0.3 , 0 , 0 , 30 ),
164186 Position = UDim2 .new (0 , 0 , 0 , 70 ),
165- TextColor3 = Color3 .fromRGB (200 , 200 , 200 ),
166- BackgroundTransparency = 1 ,
187+ BackgroundColor3 = Color3 .fromRGB (80 , 120 , 180 ),
188+ TextColor3 = Color3 .fromRGB (255 , 255 , 255 ),
189+ Event = {
190+ MouseButton1Click = function ()
191+ return setItemCount (function (current )
192+ return if current >= 5000 then 1000 else current + 1000
193+ end )
194+ end ,
195+ },
167196 }))
168197 end , { itemCount , useOptimized , triggerUpdate , renderTime }, { {
169198 elementPath = { 0 , 0 },
@@ -210,52 +239,112 @@ local function MassiveListBenchmark()
210239 propName = "Text" ,
211240 path = { 0 , 4 },
212241 } },
213- } }, "dynamic_frame_286c8u4xu " ), useFinePatchBlock (function (itemCount , items , ListComponent )
242+ } }, "dynamic_frame_l2to0lehr " ), useFinePatchBlock (function (baseLayoutOrder , virtualList , visibleCount )
214243 local _exp = {
215244 Size = UDim2 .new (1 , 0 , 1 , - 100 ),
216245 Position = UDim2 .new (0 , 0 , 0 , 100 ),
217246 BackgroundColor3 = Color3 .fromRGB (30 , 30 , 30 ),
218- CanvasSize = UDim2 .new (0 , 0 , 0 , itemCount * 45 ),
219247 ScrollBarThickness = 10 ,
220248 }
221249 local _exp_1 = React .createElement ("uilistlayout" , {
222250 SortOrder = Enum.SortOrder.LayoutOrder ,
223251 Padding = UDim .new (0 , 5 ),
224252 })
253+ local _exp_2 = useFinePatchBlock (function (baseLayoutOrder , virtualList )
254+ return React .createElement ("frame" , {
255+ LayoutOrder = baseLayoutOrder ,
256+ Size = UDim2 .new (1 , 0 , 0 , virtualList .beforeSpacerHeight ),
257+ BackgroundTransparency = 1 ,
258+ })
259+ end , { baseLayoutOrder , virtualList }, { {
260+ elementPath = {},
261+ edits = { {
262+ type = 1 ,
263+ dependencyKey = "baseLayoutOrder" ,
264+ propName = "LayoutOrder" ,
265+ path = {},
266+ }, {
267+ type = 1 ,
268+ dependencyKey = "virtualList" ,
269+ propName = "Size" ,
270+ path = {},
271+ } },
272+ } }, "dynamic_frame_96t5oo709" )
273+ local _exp_3 = virtualList .visibleItems
225274 -- ▼ ReadonlyArray.map ▼
226- local _newValue = table.create (# items )
227- local _callback = function (item , index )
275+ local _newValue = table.create (# _exp_3 )
276+ local _callback = function (virtualItem , visibleIndex )
277+ local _binding = virtualItem
278+ local item = _binding .item
228279 local _attributes = {
229- key = `item-{item .id }-{ updateTrigger } ` ,
280+ key = `item-{item .id }` ,
230281 }
231282 for _k , _v in item do
232283 _attributes [_k ] = _v
233284 end
285+ _attributes .layoutOrder = baseLayoutOrder + visibleIndex * 2 + 1
234286 return React .createElement (ListComponent , _attributes )
235287 end
236- for _k , _v in items do
237- _newValue [_k ] = _callback (_v , _k - 1 , items )
288+ for _k , _v in _exp_3 do
289+ _newValue [_k ] = _callback (_v , _k - 1 , _exp_3 )
238290 end
239291 -- ▲ ReadonlyArray.map ▲
240- return React .createElement ("scrollingframe" , _exp , _exp_1 , _newValue )
241- end , { itemCount , items , ListComponent }, { {
242- elementPath = {},
292+ return React .createElement ("scrollingframe" , _exp , _exp_1 , _exp_2 , _newValue , useFinePatchBlock (function (baseLayoutOrder , visibleCount , virtualList )
293+ return React .createElement ("frame" , {
294+ LayoutOrder = baseLayoutOrder + visibleCount * 2 + 1 ,
295+ Size = UDim2 .new (1 , 0 , 0 , virtualList .afterSpacerHeight ),
296+ BackgroundTransparency = 1 ,
297+ })
298+ end , { baseLayoutOrder , visibleCount , virtualList }, { {
299+ elementPath = {},
300+ edits = { {
301+ type = 1 ,
302+ dependencyKey = "baseLayoutOrder" ,
303+ propName = "LayoutOrder" ,
304+ path = {},
305+ }, {
306+ type = 1 ,
307+ dependencyKey = "virtualList" ,
308+ propName = "Size" ,
309+ path = {},
310+ } },
311+ } }, "dynamic_frame_ytscngc7q" ))
312+ end , { baseLayoutOrder , virtualList , visibleCount }, { {
313+ elementPath = { 0 , 1 },
243314 edits = { {
244315 type = 1 ,
245- dependencyKey = "itemCount" ,
246- propName = "CanvasSize" ,
247- path = {},
316+ dependencyKey = "baseLayoutOrder" ,
317+ propName = "LayoutOrder" ,
318+ path = { 0 , 1 },
319+ }, {
320+ type = 1 ,
321+ dependencyKey = "virtualList" ,
322+ propName = "Size" ,
323+ path = { 0 , 1 },
248324 } },
249325 }, {
250326 elementPath = { 0 },
251327 edits = { {
252328 type = 2 ,
253- dependencyKey = "items " ,
254- index = 1 ,
255- path = { 0 , 1 },
329+ dependencyKey = "virtualList " ,
330+ index = 2 ,
331+ path = { 0 , 2 },
256332 } },
257- } }, "dynamic_scrollingframe_a3yjgudsn" ))
258- end , { itemCount , useOptimized , triggerUpdate , renderTime , items , ListComponent }, { {
333+ }, {
334+ elementPath = { 0 , 3 },
335+ edits = { {
336+ type = 1 ,
337+ dependencyKey = "baseLayoutOrder" ,
338+ propName = "LayoutOrder" ,
339+ path = { 0 , 3 },
340+ }, {
341+ type = 1 ,
342+ dependencyKey = "virtualList" ,
343+ propName = "Size" ,
344+ path = { 0 , 3 },
345+ } },
346+ } }, "dynamic_scrollingframe_g68cmil6a" ))
347+ end , { itemCount , useOptimized , triggerUpdate , renderTime , baseLayoutOrder , virtualList , visibleCount }, { {
259348 elementPath = { 0 , 0 , 0 },
260349 edits = { {
261350 type = 1 ,
@@ -301,22 +390,40 @@ local function MassiveListBenchmark()
301390 path = { 0 , 0 , 4 },
302391 } },
303392 }, {
304- elementPath = { 0 , 1 },
393+ elementPath = { 0 , 1 , 1 },
305394 edits = { {
306395 type = 1 ,
307- dependencyKey = "itemCount" ,
308- propName = "CanvasSize" ,
309- path = { 0 , 1 },
396+ dependencyKey = "baseLayoutOrder" ,
397+ propName = "LayoutOrder" ,
398+ path = { 0 , 1 , 1 },
399+ }, {
400+ type = 1 ,
401+ dependencyKey = "virtualList" ,
402+ propName = "Size" ,
403+ path = { 0 , 1 , 1 },
310404 } },
311405 }, {
312406 elementPath = { 0 , 1 },
313407 edits = { {
314408 type = 2 ,
315- dependencyKey = "items" ,
316- index = 1 ,
317- path = { 0 , 1 , 1 },
409+ dependencyKey = "virtualList" ,
410+ index = 2 ,
411+ path = { 0 , 1 , 2 },
412+ } },
413+ }, {
414+ elementPath = { 0 , 1 , 3 },
415+ edits = { {
416+ type = 1 ,
417+ dependencyKey = "baseLayoutOrder" ,
418+ propName = "LayoutOrder" ,
419+ path = { 0 , 1 , 3 },
420+ }, {
421+ type = 1 ,
422+ dependencyKey = "virtualList" ,
423+ propName = "Size" ,
424+ path = { 0 , 1 , 3 },
318425 } },
319- } }, "dynamic_frame_0admk4o4r " )
426+ } }, "dynamic_frame_64biizil8 " )
320427end
321428-- Alternative ways to use @undecillion
322429-- Comment-style for arrow functions
0 commit comments