Roact error; ReplicatedStorage.Roact.createElement:53: attempt to index string with userdata - Client - createElement:53

Error

17:39:17.492 ReplicatedStorage.Roact.createElement:53: attempt to index string with userdata - Client - createElement:53
17:39:17.492 Stack Begin - Studio
17:39:17.492 Script ‘ReplicatedStorage.Roact.createElement’, Line 53 - function createElement - Studio - createElement:53
17:39:17.492 Script ‘Players.AbstractCo.PlayerScripts.InventoryClient.InventoryUIComponents.Slot’, Line 49 - function render - Studio - Slot:49
17:39:17.493 Script ‘ReplicatedStorage.Roact.Component’, Line 330 - function __mount - Studio - Component:330
17:39:17.493 Script ‘ReplicatedStorage.Roact.createReconciler’, Line 363 - function mountVirtualNode - Studio - createReconciler:363
17:39:17.493 Script ‘ReplicatedStorage.Roact.createReconciler’, Line 93 - function updateChildren - Studio - createReconciler:93
17:39:17.493 Script ‘ReplicatedStorage.Roact.createReconciler’, Line 111 - function updateVirtualNodeWithChildren - Studio - createReconciler:111
17:39:17.493 Script ‘ReplicatedStorage.Roact.RobloxRenderer’, Line 273 - function updateHostNode - Studio - RobloxRenderer:273
17:39:17.493 Script ‘ReplicatedStorage.Roact.createReconciler’, Line 234 - function updateVirtualNode - Studio - createReconciler:234
17:39:17.493 Script ‘ReplicatedStorage.Roact.createReconciler’, Line 72 - function updateChildren - Studio - createReconciler:72
17:39:17.493 Script ‘ReplicatedStorage.Roact.createReconciler’, Line 119 - function updateVirtualNodeWithRenderResult - Studio - createReconciler:119
17:39:17.493 Script ‘ReplicatedStorage.Roact.Component’, Line 496 - function __resolveUpdate - Studio - Component:496
17:39:17.493 Script ‘ReplicatedStorage.Roact.Component’, Line 434 - function __update - Studio - Component:434
17:39:17.493 Script ‘ReplicatedStorage.Roact.Component’, Line 161 - function setState - Studio - Component:161
17:39:17.493 Script ‘Players.AbstractCo.PlayerScripts.InventoryClient.InventoryUIComponents.InventoryFrame’, Line 56 - function SendInventoryData - Studio - InventoryFrame:56
17:39:17.493 Script ‘Players.AbstractCo.PlayerScripts.InventoryClient.InventoryUIComponents.InventoryFrame’, Line 102 - Studio - InventoryFrame:102
17:39:17.494 Stack End - Studio

Code:

InventoryFrame
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Roact = require(ReplicatedStorage:WaitForChild("Roact"))

local Root = script.Parent.Parent
local InventoryClientManager = nil

local Slot = nil

local InventoryShared = require(ReplicatedStorage:WaitForChild("InventoryShared"))

-- UTILITIES
local TableUtil = require(InventoryShared.Util:WaitForChild("TableUtil"))
local getTableCount = TableUtil.GetTableCount

-- CONFIG
local CONFIG = InventoryShared.CONFIG
local DEFAULT_INVENTORY_UI_LAYOUT = CONFIG.DEFAULT_INVENTORY_UI_LAYOUT
local DEFAULT_INVENTORY_UI_LAYOUT_PROPERTIES = CONFIG.DEFAULT_INVENTORY_UI_LAYOUT_PROPERTIES


----------------------------------------------------------------------------
-- COMPONENT DECLARATION

InventoryFrameComponent = Roact.Component:extend("InventoryFrame")


----------------------------------------------------------------------------
-- wrapper; this is what is returned

local wrapper = {}

local _init_done = false

function wrapper:Init()
	assert(_init_done == false, "Cannot reinit the InventoryFrame")
	InventoryClientManager = require(Root:WaitForChild("InventoryClientManager"))
	Slot = require(script.Parent:WaitForChild("Slot")):GetComponent()
	_init_done = true
end

function wrapper:GetComponent()
	return InventoryFrameComponent
end

----------------------------------------------------------------------------
-- COMPONENT CODE

function InventoryFrameComponent:init()
	local currentSlotData = InventoryClientManager:RequestInventoryData(self.props.InventoryId)
	self:setState({
		Slots = currentSlotData
	})
end

function InventoryFrameComponent:SendInventoryData(slotData)
	self:setState({
		Slots = slotData
	})
end

function InventoryFrameComponent:render()
	local inventoryData = self.state.Slots or {}
	local children = {}
	--[[
	local InventoryFrameComponentConfig = self.props.Config or {}

	-- UI layout
	if InventoryFrameComponentConfig.UILayout then
		local cls = InventoryFrameComponentConfig.UILayout
		local props = InventoryFrameComponentConfig.UILayoutProperties
		if cls == "Default" or cls == true then
			cls = DEFAULT_INVENTORY_UI_LAYOUT
			props = DEFAULT_INVENTORY_UI_LAYOUT_PROPERTIES
		else
			assert(props, string.format("UILayoutProperties is required in config if the UILayout is enabled and is not set to true or default"))
			assert(type(props) == "table", string.format("Invalid type of UILayoutProperties, table required, got %s.", type(props)))
		end
		children.UILayout = Roact.createElement(cls, props)
	end
	--]]
	-- slots
	for i, v in pairs(inventoryData) do
		v = v or {_blank = true}
		local slotProps = {
			SlotData = v
		}
		local newSlot = Roact.createElement(Slot, slotProps)
		children[i] = newSlot
	end

	-- this prevents the 3rd argument from being a empty table, thus causing a error
	if getTableCount(children) == 0 then children = nil end

	return Roact.createElement(self.props.FrameClass, self.props.Description, children)
end


function InventoryFrameComponent:didMount()
	-- add the events for the InventoryClientManager
	self._updateConn = InventoryClientManager.InventoryUpdated:Connect(function(inventoryId, slotData)
		if self.props.InventoryId ~= inventoryId then return end
		self:SendInventoryData(slotData)
	end)
end


function InventoryFrameComponent:willUnmount()
	local updateConn = self._updateConn
	if updateConn then
		updateConn:Disconnect()
	end
end

return wrapper
Slot
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Roact = require(ReplicatedStorage:WaitForChild("Roact"))

local Root = script.Parent.Parent

local InventoryShared = require(ReplicatedStorage:WaitForChild("InventoryShared"))

-- UTILITIES
local TableUtil = require(InventoryShared.Util:WaitForChild("TableUtil"))
local getTableCount = TableUtil.GetTableCount

-- CONFIG
local CONFIG = InventoryShared.CONFIG
local SLOT_FRAME_CLASS = CONFIG.SLOT_FRAME_CLASS
local SLOT_FRAME_DESCRIPTION = CONFIG.SLOT_FRAME_DESCRIPTION
local SLOT_BACKGROUND_DESCRIPTION = CONFIG.SLOT_BACKGROUND_DESCRIPTION


----------------------------------------------------------------------------
-- COMPONENT DECLARATION

Slot = Roact.Component:extend("Slot")


----------------------------------------------------------------------------
-- wrapper; this is what is returned

local wrapper = {}

local _init_done = false

function wrapper:Init()
	assert(_init_done == false, "Cannot reinit the InventoryFrame")
	
	_init_done = true
end

function wrapper:GetComponent()
	return Slot
end


----------------------------------------------------------------------------
-- COMPONENT CODE


function Slot:render()
	local children = nil
	return Roact:createElement("Frame", {
		Size = UDim2.new(0, 0, 0, 0)
	})
	--return Roact:createElement("Frame", {})
	--return Roact:createElement(SLOT_FRAME_CLASS, {}, children)
end


return wrapper
InventoryUIComponents
local InventoryUIComponents = {}

local _init_done = false

function InventoryUIComponents:Init()
	assert(_init_done == false, "Cannot reinit the InventoryUIComponents")
	for _,v in pairs(script:GetChildren()) do
		InventoryUIComponents = require(v)
		InventoryUIComponents:Init()
		self[v.Name] = InventoryUIComponents:GetComponent()
	end
end

return InventoryUIComponents

Things to note:

  • The InventoryFrame and Slot are Parented to the InventoryUIComponents
  • THe InventoryUIComponents is Initialized by its parent, the InventoryClient
  • The Whole scripts are pasted, so finding the line numbers can be done by simply counting.