createElement mutates props when passing in children
I overlooked this when I first implemented createElement, but the passed in props table is mutated if children is also passed in:
https://github.com/Roblox/roact/blob/394e9fa918d4e0a0a7dddd344487498e9b912897/lib/Core.lua#L152-L158
This is fine if we document that Roact takes ownership of your props table when you pass it in, but I think it could potentially cause confusing behavior if a user tries to re-use props and notices children persisting (or warnings popping up).
I don't really want to copy the entire props table on every createElement since it needs to be as light and fast as possible for cheap reconciliation to be a reality.
This seems like something we should document. I'm pretty sure most (all?) uses of Roact in the wild don't re-use property tables, so..
If we feel the need to not nail this down as an API dependency, one option is to use __index:
if children then
props = setmetatable({
[Core.Children] = children,
}, {
__index = props
})
end
Metatables would be a good way to prevent mutation if wanted to go down that route.
As I've thought about it, I think documentation might be the best way forward.
One thought I had on this: if someone wants to re-use the props table across multiple different components, they'll get weird behavior:
local SHARED_PROPS = {
-- ...
}
local function SomeComponent()
return Roact.createElement("Frame", SHARED_PROPS, {
-- some form of unique children
})
end
local function OtherComponent()
return Roact.createElement("Frame", SHARED_PROPS, {
-- different children from SomeComponent
})
end
This causes problems. I'm not sure if this is a paradigm to support, though - this example is kind of forced. This can be fixed really easily from a user point of view by copying SHARED_PROPS in each createElement call, but that's not super obvious...
I think documentation is probably the best option here; if it becomes a major problem we can move to a metatable-based proxy approach.