From ad8c5c0e42b0a1678d644c9a9f4840c127323052 Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Tue, 20 Nov 2018 15:23:52 -0800 Subject: [PATCH 01/11] Broken stuff, but the right direction --- lib/ChildUtils.lua | 14 ++++++-------- lib/Type.lua | 3 ++- lib/createFragment.lua | 10 ++++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 lib/createFragment.lua diff --git a/lib/ChildUtils.lua b/lib/ChildUtils.lua index f81a12ac..960f41bb 100644 --- a/lib/ChildUtils.lua +++ b/lib/ChildUtils.lua @@ -39,8 +39,12 @@ ChildUtils.UseParentKey = Symbol.named("UseParentKey") function ChildUtils.iterateChildren(childrenOrChild) local richType = Type.of(childrenOrChild) - -- Single child, the simplest case! - if richType ~= nil then + if richType == Type.Fragment then + return pairs(childrenOrChild.elements) + end + + -- Single child + if richType == Type.Element then local called = false return function() @@ -55,12 +59,6 @@ function ChildUtils.iterateChildren(childrenOrChild) local regularType = typeof(childrenOrChild) - -- A dictionary of children, hopefully! - -- TODO: Is this too flaky? Should we introduce a Fragment type like React? - if regularType == "table" then - return pairs(childrenOrChild) - end - if childrenOrChild == nil or regularType == "boolean" then return noop end diff --git a/lib/Type.lua b/lib/Type.lua index ca3040d1..55acd213 100644 --- a/lib/Type.lua +++ b/lib/Type.lua @@ -20,10 +20,11 @@ local function addType(name) TypeInternal[name] = Symbol.named("Roact" .. name) end +addType("Binding") addType("Element") +addType("Fragment") addType("HostChangeEvent") addType("HostEvent") -addType("Binding") addType("StatefulComponentClass") addType("StatefulComponentInstance") addType("VirtualNode") diff --git a/lib/createFragment.lua b/lib/createFragment.lua new file mode 100644 index 00000000..3d0ceb0c --- /dev/null +++ b/lib/createFragment.lua @@ -0,0 +1,10 @@ +local Type = require(script.Parent.Type) + +local function createFragment(elements) + return { + [Type] = Type.Fragment, + elements = elements, + } +end + +return createFragment \ No newline at end of file From 5242beb927fce19a1905dff3b249565d9269c000 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Fri, 30 Nov 2018 11:45:55 -0800 Subject: [PATCH 02/11] Rename iterateChildren to iterateElements --- lib/ChildUtils.lua | 26 +++++++++++++------------- lib/ChildUtils.spec.lua | 12 ++++++------ lib/createReconciler.lua | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/ChildUtils.lua b/lib/ChildUtils.lua index 960f41bb..949abe14 100644 --- a/lib/ChildUtils.lua +++ b/lib/ChildUtils.lua @@ -18,29 +18,29 @@ ChildUtils.UseParentKey = Symbol.named("UseParentKey") --[[ Returns an iterator over the children of an element. - `childrenOrChild` may be one of: + `elementOrElements` may be one of: * a boolean * nil * a single element * a table of elements - If `childrenOrChild` is a boolean or nil, this will return an iterator with + If `elementOrElements` is a boolean or nil, this will return an iterator with zero elements. - If `childrenOrChild` is a single element, this will return an iterator with + If `elementOrElements` is a single element, this will return an iterator with one element: a tuple where the first value is ChildUtils.UseParentKey, and - the second is the value of `childrenOrChild`. + the second is the value of `elementOrElements`. - If `childrenOrChild` is a table, this will return an iterator over all the - elements of the array, equivalent to `pairs(childrenOrChild)`. + If `elementOrElements` is a table, this will return an iterator over all the + elements of the array, equivalent to `pairs(elementOrElements)`. - If `childrenOrChild` is none of the above, this function will throw. + If `elementOrElements` is none of the above, this function will throw. ]] -function ChildUtils.iterateChildren(childrenOrChild) - local richType = Type.of(childrenOrChild) +function ChildUtils.iterateElements(elementOrElements) + local richType = Type.of(elementOrElements) if richType == Type.Fragment then - return pairs(childrenOrChild.elements) + return pairs(elementOrElements.elements) end -- Single child @@ -52,14 +52,14 @@ function ChildUtils.iterateChildren(childrenOrChild) return nil else called = true - return ChildUtils.UseParentKey, childrenOrChild + return ChildUtils.UseParentKey, elementOrElements end end end - local regularType = typeof(childrenOrChild) + local regularType = typeof(elementOrElements) - if childrenOrChild == nil or regularType == "boolean" then + if elementOrElements == nil or regularType == "boolean" then return noop end diff --git a/lib/ChildUtils.spec.lua b/lib/ChildUtils.spec.lua index cedbec32..4b8cc654 100644 --- a/lib/ChildUtils.spec.lua +++ b/lib/ChildUtils.spec.lua @@ -3,10 +3,10 @@ return function() local createElement = require(script.Parent.createElement) local Type = require(script.Parent.Type) - describe("iterateChildren", function() + describe("iterateElements", function() it("should iterate once for a single child", function() local child = createElement("TextLabel") - local iterator = ChildUtils.iterateChildren(child) + local iterator = ChildUtils.iterateElements(child) local iteratedKey, iteratedChild = iterator() -- For single elements, the key should be UseParentKey expect(iteratedKey).to.equal(ChildUtils.UseParentKey) @@ -25,7 +25,7 @@ return function() local seenChildren = {} local count = 0 - for key, child in ChildUtils.iterateChildren(children) do + for key, child in ChildUtils.iterateElements(children) do expect(typeof(key)).to.equal("string") expect(Type.of(child)).to.equal(Type.Element) seenChildren[child] = key @@ -38,18 +38,18 @@ return function() end) it("should return a zero-element iterator for booleans", function() - local booleanIterator = ChildUtils.iterateChildren(false) + local booleanIterator = ChildUtils.iterateElements(false) expect(booleanIterator()).to.equal(nil) end) it("should return a zero-element iterator for nil", function() - local nilIterator = ChildUtils.iterateChildren(nil) + local nilIterator = ChildUtils.iterateElements(nil) expect(nilIterator()).to.equal(nil) end) it("should throw if given an illegal value", function() expect(function() - ChildUtils.iterateChildren(1) + ChildUtils.iterateElements(1) end).to.throw() end) end) diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index ebbfe0f9..530d0fc9 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -71,7 +71,7 @@ local function createReconciler(renderer) end -- Added children - for childKey, newElement in ChildUtils.iterateChildren(newChildElements) do + for childKey, newElement in ChildUtils.iterateElements(newChildElements) do local concreteKey = childKey if childKey == ChildUtils.UseParentKey then concreteKey = virtualNode.hostKey From fd3fb28173a14e663890b298f92ae879875b6ae5 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Fri, 30 Nov 2018 13:01:28 -0800 Subject: [PATCH 03/11] Reintroduce table processing for iterating over elements --- lib/ChildUtils.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/ChildUtils.lua b/lib/ChildUtils.lua index 949abe14..8235a7ec 100644 --- a/lib/ChildUtils.lua +++ b/lib/ChildUtils.lua @@ -22,6 +22,7 @@ ChildUtils.UseParentKey = Symbol.named("UseParentKey") * a boolean * nil * a single element + * a fragment * a table of elements If `elementOrElements` is a boolean or nil, this will return an iterator with @@ -31,8 +32,8 @@ ChildUtils.UseParentKey = Symbol.named("UseParentKey") one element: a tuple where the first value is ChildUtils.UseParentKey, and the second is the value of `elementOrElements`. - If `elementOrElements` is a table, this will return an iterator over all the - elements of the array, equivalent to `pairs(elementOrElements)`. + If `elementOrElements` is a fragment or a table, this will return an iterator + over all the elements of the array. If `elementOrElements` is none of the above, this function will throw. ]] @@ -59,6 +60,12 @@ function ChildUtils.iterateElements(elementOrElements) local regularType = typeof(elementOrElements) + -- This is the format we expect for the children of an element + -- provided when calling `createElement` + if regularType == "table" then + return pairs(elementOrElements) + end + if elementOrElements == nil or regularType == "boolean" then return noop end From 1cc04c55cc9e379e29b3861fd9360a161c8299b5 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Fri, 30 Nov 2018 15:25:07 -0800 Subject: [PATCH 04/11] Split updateVirtualNodeChildren into distinct functions --- lib/ChildUtils.lua | 8 +++++- lib/ChildUtils.spec.lua | 21 ++++++++------- lib/Component.lua | 4 +-- lib/RobloxRenderer.lua | 2 ++ lib/createReconciler.lua | 49 +++++++++++++++++++++++++++++++---- lib/createReconciler.spec.lua | 7 ++--- 6 files changed, 70 insertions(+), 21 deletions(-) diff --git a/lib/ChildUtils.lua b/lib/ChildUtils.lua index 8235a7ec..d7aba4fb 100644 --- a/lib/ChildUtils.lua +++ b/lib/ChildUtils.lua @@ -95,7 +95,13 @@ function ChildUtils.getChildByKey(elements, hostKey) return nil end - return elements[hostKey] + if Type.of(elements) == Type.Fragment then + return elements.elements[hostKey] + end + + error("Invalid elements") + -- TODO: Let's fix this case; it should work for fragments(?), but not arbitrary tables + -- return elements[hostKey] end return ChildUtils \ No newline at end of file diff --git a/lib/ChildUtils.spec.lua b/lib/ChildUtils.spec.lua index 4b8cc654..2c8f057c 100644 --- a/lib/ChildUtils.spec.lua +++ b/lib/ChildUtils.spec.lua @@ -1,6 +1,7 @@ return function() local ChildUtils = require(script.Parent.ChildUtils) local createElement = require(script.Parent.createElement) + local createFragment = require(script.Parent.createFragment) local Type = require(script.Parent.Type) describe("iterateElements", function() @@ -16,11 +17,11 @@ return function() expect(iteratedKey).to.equal(nil) end) - it("should iterate over multiple children", function() - local children = { + it("should iterate over fragments", function() + local children = createFragment({ a = createElement("TextLabel"), b = createElement("TextLabel"), - } + }) local seenChildren = {} local count = 0 @@ -33,8 +34,8 @@ return function() end expect(count).to.equal(2) - expect(seenChildren[children.a]).to.equal("a") - expect(seenChildren[children.b]).to.equal("b") + expect(seenChildren[children.elements.a]).to.equal("a") + expect(seenChildren[children.elements.b]).to.equal("b") end) it("should return a zero-element iterator for booleans", function() @@ -76,17 +77,17 @@ return function() end) it("should return the corresponding element", function() - local children = { + local children = createFragment({ a = createElement("TextLabel"), b = createElement("TextLabel"), - } + }) - expect(ChildUtils.getChildByKey(children, "a")).to.equal(children.a) - expect(ChildUtils.getChildByKey(children, "b")).to.equal(children.b) + expect(ChildUtils.getChildByKey(children, "a")).to.equal(children.elements.a) + expect(ChildUtils.getChildByKey(children, "b")).to.equal(children.elements.b) end) it("should return nil if the key does not exist", function() - local children = {} + local children = createFragment({}) expect(ChildUtils.getChildByKey(children, "a")).to.equal(nil) end) diff --git a/lib/Component.lua b/lib/Component.lua index 25ec2b68..46ceca98 100644 --- a/lib/Component.lua +++ b/lib/Component.lua @@ -188,7 +188,7 @@ function Component:__mount(reconciler, virtualNode) local children = instance:render() internalData.setStateBlockedReason = nil - reconciler.updateVirtualNodeChildren(virtualNode, hostParent, children) + reconciler.updateVirtualNodeWithElements(virtualNode, hostParent, children) if instance.didMount ~= nil then instance:didMount() @@ -289,7 +289,7 @@ function Component:__update(updatedElement, updatedState) local renderResult = virtualNode.instance:render() internalData.setStateBlockedReason = nil - reconciler.updateVirtualNodeChildren(virtualNode, virtualNode.hostParent, renderResult) + reconciler.updateVirtualNodeWithElements(virtualNode, virtualNode.hostParent, renderResult) if self.didUpdate ~= nil then self:didUpdate(oldProps, oldState) diff --git a/lib/RobloxRenderer.lua b/lib/RobloxRenderer.lua index a6acb88a..35fa1130 100644 --- a/lib/RobloxRenderer.lua +++ b/lib/RobloxRenderer.lua @@ -141,6 +141,7 @@ function RobloxRenderer.mountHostNode(reconciler, virtualNode) local children = element.props[Children] + -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result reconciler.updateVirtualNodeChildren(virtualNode, virtualNode.hostObject, children) instance.Parent = hostParent @@ -199,6 +200,7 @@ function RobloxRenderer.updateHostNode(reconciler, virtualNode, newElement) end end + -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result reconciler.updateVirtualNodeChildren(virtualNode, virtualNode.hostObject, newElement.props[Children]) -- Resume event firing now that everything's updated successfully diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index 530d0fc9..6df66f41 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -49,14 +49,14 @@ local function createReconciler(renderer) Utility to update the children of a virtual node based on zero or more updated children given as elements. ]] - local function updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) + local function updateVirtualNodeChildrenInternal(virtualNode, hostParent, childContainer) assert(Type.of(virtualNode) == Type.VirtualNode) local removeKeys = {} -- Changed or removed children for childKey, childNode in pairs(virtualNode.children) do - local newElement = ChildUtils.getChildByKey(newChildElements, childKey) + local newElement = childContainer.getChildByKey(childKey) local newNode = updateVirtualNode(childNode, newElement) if newNode ~= nil then @@ -71,7 +71,7 @@ local function createReconciler(renderer) end -- Added children - for childKey, newElement in ChildUtils.iterateElements(newChildElements) do + for childKey, newElement in childContainer.getIterator() do local concreteKey = childKey if childKey == ChildUtils.UseParentKey then concreteKey = virtualNode.hostKey @@ -89,6 +89,42 @@ local function createReconciler(renderer) end end + --[[ + Utility to update the children of a virtual node based on zero or more + updated children given as elements. + ]] + local function updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) + updateVirtualNodeChildrenInternal(virtualNode, hostParent, { + getChildByKey = function(key) + return newChildElements[key] + end, + getIterator = function() + if newChildElements ~= nil then + return pairs(newChildElements) + else + return function() + return nil + end + end + end, + }) + end + + --[[ + Utility to update the children of a virtual node based on zero or more + updated children given as an element or a fragment + ]] + local function updateVirtualNodeWithElements(virtualNode, hostParent, newChildElements) + updateVirtualNodeChildrenInternal(virtualNode, hostParent, { + getChildByKey = function(key) + return ChildUtils.getChildByKey(newChildElements, key) + end, + getIterator = function() + return ChildUtils.iterateElements(newChildElements) + end, + }) + end + --[[ Unmounts the given virtual node and releases any held resources. ]] @@ -117,7 +153,7 @@ local function createReconciler(renderer) local function updateFunctionVirtualNode(virtualNode, newElement) local children = newElement.component(newElement.props) - updateVirtualNodeChildren(virtualNode, virtualNode.hostParent, children) + updateVirtualNodeWithElements(virtualNode, virtualNode.hostParent, children) return virtualNode end @@ -140,6 +176,7 @@ local function createReconciler(renderer) local children = newElement.props[Children] + -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result updateVirtualNodeChildren(virtualNode, targetHostParent, children) return virtualNode @@ -237,7 +274,7 @@ local function createReconciler(renderer) local children = element.component(element.props) - updateVirtualNodeChildren(virtualNode, virtualNode.hostParent, children) + updateVirtualNodeWithElements(virtualNode, virtualNode.hostParent, children) end local function mountPortalVirtualNode(virtualNode) @@ -248,6 +285,7 @@ local function createReconciler(renderer) assert(renderer.isHostObject(targetHostParent)) + -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result updateVirtualNodeChildren(virtualNode, targetHostParent, children) end @@ -354,6 +392,7 @@ local function createReconciler(renderer) unmountVirtualNode = unmountVirtualNode, updateVirtualNode = updateVirtualNode, updateVirtualNodeChildren = updateVirtualNodeChildren, + updateVirtualNodeWithElements = updateVirtualNodeWithElements, } return reconciler diff --git a/lib/createReconciler.spec.lua b/lib/createReconciler.spec.lua index fc4a0309..528ef04a 100644 --- a/lib/createReconciler.spec.lua +++ b/lib/createReconciler.spec.lua @@ -1,6 +1,7 @@ return function() local assign = require(script.Parent.assign) local createElement = require(script.Parent.createElement) + local createFragment = require(script.Parent.createFragment) local createSpy = require(script.Parent.createSpy) local NoopRenderer = require(script.Parent.NoopRenderer) local Type = require(script.Parent.Type) @@ -198,7 +199,7 @@ return function() expect(childComponentSpy.callCount).to.equal(1) end) - it("should mount multiple children of function components", function() + it("should mount fragments returned by function components", function() local childAComponentSpy = createSpy(function(props) return nil end) @@ -208,14 +209,14 @@ return function() end) local parentComponentSpy = createSpy(function(props) - return { + return createFragment({ A = createElement(childAComponentSpy.value, { value = props.value + 1, }), B = createElement(childBComponentSpy.value, { value = props.value + 5, }), - } + }) end) local element = createElement(parentComponentSpy.value, { From 76f3679475e55f68e188f2e8bb81fbe84de4af92 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Fri, 30 Nov 2018 15:34:04 -0800 Subject: [PATCH 05/11] Extreme renaming --- lib/{ChildUtils.lua => ElementUtils.lua} | 18 +++---- ...ldUtils.spec.lua => ElementUtils.spec.lua} | 30 +++++------ lib/createReconciler.lua | 50 +++++++++---------- 3 files changed, 47 insertions(+), 51 deletions(-) rename lib/{ChildUtils.lua => ElementUtils.lua} (84%) rename lib/{ChildUtils.spec.lua => ElementUtils.spec.lua} (68%) diff --git a/lib/ChildUtils.lua b/lib/ElementUtils.lua similarity index 84% rename from lib/ChildUtils.lua rename to lib/ElementUtils.lua index d7aba4fb..838cf0b9 100644 --- a/lib/ChildUtils.lua +++ b/lib/ElementUtils.lua @@ -5,7 +5,7 @@ local function noop() return nil end -local ChildUtils = {} +local ElementUtils = {} --[[ A signal value indicating that a child should use its parent's key, because @@ -14,7 +14,7 @@ local ChildUtils = {} This occurs when you return only one element from a function component or stateful render function. ]] -ChildUtils.UseParentKey = Symbol.named("UseParentKey") +ElementUtils.UseParentKey = Symbol.named("UseParentKey") --[[ Returns an iterator over the children of an element. @@ -29,7 +29,7 @@ ChildUtils.UseParentKey = Symbol.named("UseParentKey") zero elements. If `elementOrElements` is a single element, this will return an iterator with - one element: a tuple where the first value is ChildUtils.UseParentKey, and + one element: a tuple where the first value is ElementUtils.UseParentKey, and the second is the value of `elementOrElements`. If `elementOrElements` is a fragment or a table, this will return an iterator @@ -37,7 +37,7 @@ ChildUtils.UseParentKey = Symbol.named("UseParentKey") If `elementOrElements` is none of the above, this function will throw. ]] -function ChildUtils.iterateElements(elementOrElements) +function ElementUtils.iterateElements(elementOrElements) local richType = Type.of(elementOrElements) if richType == Type.Fragment then @@ -53,7 +53,7 @@ function ChildUtils.iterateElements(elementOrElements) return nil else called = true - return ChildUtils.UseParentKey, elementOrElements + return ElementUtils.UseParentKey, elementOrElements end end end @@ -79,16 +79,16 @@ end * If `elements` is nil or a boolean, this will return `nil`, regardless of the key given. * If `elements` is a single element, this will return `nil`, unless the key - is ChildUtils.UseParentKey. + is ElementUtils.UseParentKey. * If `elements` is a table of elements, this will return `elements[key]`. ]] -function ChildUtils.getChildByKey(elements, hostKey) +function ElementUtils.getElementByKey(elements, hostKey) if elements == nil or typeof(elements) == "boolean" then return nil end if Type.of(elements) == Type.Element then - if hostKey == ChildUtils.UseParentKey then + if hostKey == ElementUtils.UseParentKey then return elements end @@ -104,4 +104,4 @@ function ChildUtils.getChildByKey(elements, hostKey) -- return elements[hostKey] end -return ChildUtils \ No newline at end of file +return ElementUtils \ No newline at end of file diff --git a/lib/ChildUtils.spec.lua b/lib/ElementUtils.spec.lua similarity index 68% rename from lib/ChildUtils.spec.lua rename to lib/ElementUtils.spec.lua index 2c8f057c..a909fa7d 100644 --- a/lib/ChildUtils.spec.lua +++ b/lib/ElementUtils.spec.lua @@ -1,5 +1,5 @@ return function() - local ChildUtils = require(script.Parent.ChildUtils) + local ElementUtils = require(script.Parent.ElementUtils) local createElement = require(script.Parent.createElement) local createFragment = require(script.Parent.createFragment) local Type = require(script.Parent.Type) @@ -7,10 +7,10 @@ return function() describe("iterateElements", function() it("should iterate once for a single child", function() local child = createElement("TextLabel") - local iterator = ChildUtils.iterateElements(child) + local iterator = ElementUtils.iterateElements(child) local iteratedKey, iteratedChild = iterator() -- For single elements, the key should be UseParentKey - expect(iteratedKey).to.equal(ChildUtils.UseParentKey) + expect(iteratedKey).to.equal(ElementUtils.UseParentKey) expect(iteratedChild).to.equal(child) iteratedKey = iterator() @@ -26,7 +26,7 @@ return function() local seenChildren = {} local count = 0 - for key, child in ChildUtils.iterateElements(children) do + for key, child in ElementUtils.iterateElements(children) do expect(typeof(key)).to.equal("string") expect(Type.of(child)).to.equal(Type.Element) seenChildren[child] = key @@ -39,40 +39,40 @@ return function() end) it("should return a zero-element iterator for booleans", function() - local booleanIterator = ChildUtils.iterateElements(false) + local booleanIterator = ElementUtils.iterateElements(false) expect(booleanIterator()).to.equal(nil) end) it("should return a zero-element iterator for nil", function() - local nilIterator = ChildUtils.iterateElements(nil) + local nilIterator = ElementUtils.iterateElements(nil) expect(nilIterator()).to.equal(nil) end) it("should throw if given an illegal value", function() expect(function() - ChildUtils.iterateElements(1) + ElementUtils.iterateElements(1) end).to.throw() end) end) - describe("getChildByKey", function() + describe("getElementByKey", function() it("should return nil for booleans", function() - expect(ChildUtils.getChildByKey(true, "test")).to.equal(nil) + expect(ElementUtils.getElementByKey(true, "test")).to.equal(nil) end) it("should return nil for nil", function() - expect(ChildUtils.getChildByKey(nil, "test")).to.equal(nil) + expect(ElementUtils.getElementByKey(nil, "test")).to.equal(nil) end) describe("single elements", function() local element = createElement("TextLabel") it("should return the element if the key is UseParentKey", function() - expect(ChildUtils.getChildByKey(element, ChildUtils.UseParentKey)).to.equal(element) + expect(ElementUtils.getElementByKey(element, ElementUtils.UseParentKey)).to.equal(element) end) it("should return nil if the key is not UseParentKey", function() - expect(ChildUtils.getChildByKey(element, "test")).to.equal(nil) + expect(ElementUtils.getElementByKey(element, "test")).to.equal(nil) end) end) @@ -82,14 +82,14 @@ return function() b = createElement("TextLabel"), }) - expect(ChildUtils.getChildByKey(children, "a")).to.equal(children.elements.a) - expect(ChildUtils.getChildByKey(children, "b")).to.equal(children.elements.b) + expect(ElementUtils.getElementByKey(children, "a")).to.equal(children.elements.a) + expect(ElementUtils.getElementByKey(children, "b")).to.equal(children.elements.b) end) it("should return nil if the key does not exist", function() local children = createFragment({}) - expect(ChildUtils.getChildByKey(children, "a")).to.equal(nil) + expect(ElementUtils.getElementByKey(children, "a")).to.equal(nil) end) end) end \ No newline at end of file diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index 6df66f41..01bd44ba 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -1,9 +1,25 @@ local Type = require(script.Parent.Type) local ElementKind = require(script.Parent.ElementKind) -local ChildUtils = require(script.Parent.ChildUtils) +local ElementUtils = require(script.Parent.ElementUtils) local Children = require(script.Parent.PropMarkers.Children) local Logging = require(script.Parent.Logging) +local ElementTable = {} + +function ElementTable.getElementByKey(elements, key) + return elements[key] +end + +function ElementTable.iterateElements(elements) + if elements ~= nil then + return pairs(elements) + else + return function() + return nil + end + end +end + --[[ The reconciler is the mechanism in Roact that constructs the virtual tree that later gets turned into concrete objects by the renderer. @@ -49,14 +65,14 @@ local function createReconciler(renderer) Utility to update the children of a virtual node based on zero or more updated children given as elements. ]] - local function updateVirtualNodeChildrenInternal(virtualNode, hostParent, childContainer) + local function updateVirtualNodeChildrenInternal(virtualNode, hostParent, newChildElements, utils) assert(Type.of(virtualNode) == Type.VirtualNode) local removeKeys = {} -- Changed or removed children for childKey, childNode in pairs(virtualNode.children) do - local newElement = childContainer.getChildByKey(childKey) + local newElement = utils.getElementByKey(newChildElements, childKey) local newNode = updateVirtualNode(childNode, newElement) if newNode ~= nil then @@ -71,9 +87,9 @@ local function createReconciler(renderer) end -- Added children - for childKey, newElement in childContainer.getIterator() do + for childKey, newElement in utils.iterateElements(newChildElements) do local concreteKey = childKey - if childKey == ChildUtils.UseParentKey then + if childKey == ElementUtils.UseParentKey then concreteKey = virtualNode.hostKey end @@ -94,20 +110,7 @@ local function createReconciler(renderer) updated children given as elements. ]] local function updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) - updateVirtualNodeChildrenInternal(virtualNode, hostParent, { - getChildByKey = function(key) - return newChildElements[key] - end, - getIterator = function() - if newChildElements ~= nil then - return pairs(newChildElements) - else - return function() - return nil - end - end - end, - }) + updateVirtualNodeChildrenInternal(virtualNode, hostParent, newChildElements, ElementTable) end --[[ @@ -115,14 +118,7 @@ local function createReconciler(renderer) updated children given as an element or a fragment ]] local function updateVirtualNodeWithElements(virtualNode, hostParent, newChildElements) - updateVirtualNodeChildrenInternal(virtualNode, hostParent, { - getChildByKey = function(key) - return ChildUtils.getChildByKey(newChildElements, key) - end, - getIterator = function() - return ChildUtils.iterateElements(newChildElements) - end, - }) + updateVirtualNodeChildrenInternal(virtualNode, hostParent, newChildElements, ElementUtils) end --[[ From a684b853c0d69c21cf921b76b53b1d1a95de8fe3 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Fri, 30 Nov 2018 15:50:59 -0800 Subject: [PATCH 06/11] Remove unintended handling of table case --- lib/ElementUtils.lua | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/ElementUtils.lua b/lib/ElementUtils.lua index 838cf0b9..1385335a 100644 --- a/lib/ElementUtils.lua +++ b/lib/ElementUtils.lua @@ -60,12 +60,6 @@ function ElementUtils.iterateElements(elementOrElements) local regularType = typeof(elementOrElements) - -- This is the format we expect for the children of an element - -- provided when calling `createElement` - if regularType == "table" then - return pairs(elementOrElements) - end - if elementOrElements == nil or regularType == "boolean" then return noop end @@ -100,8 +94,6 @@ function ElementUtils.getElementByKey(elements, hostKey) end error("Invalid elements") - -- TODO: Let's fix this case; it should work for fragments(?), but not arbitrary tables - -- return elements[hostKey] end return ElementUtils \ No newline at end of file From b9f434d9f28ee17d02c7574d46ef0006d40d23e6 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Fri, 30 Nov 2018 17:52:43 -0800 Subject: [PATCH 07/11] More renaming nonsense, some reorganizing, test to make sure invalid elements throw --- lib/Component.lua | 4 +-- lib/ElementUtils.lua | 10 ++++++- lib/RobloxRenderer.lua | 2 -- lib/createReconciler.lua | 55 +++++++++++------------------------ lib/createReconciler.spec.lua | 28 ++++++++++++++++++ 5 files changed, 56 insertions(+), 43 deletions(-) diff --git a/lib/Component.lua b/lib/Component.lua index 46ceca98..90f9dec2 100644 --- a/lib/Component.lua +++ b/lib/Component.lua @@ -188,7 +188,7 @@ function Component:__mount(reconciler, virtualNode) local children = instance:render() internalData.setStateBlockedReason = nil - reconciler.updateVirtualNodeWithElements(virtualNode, hostParent, children) + reconciler.updateVirtualNodeChildrenFromElements(virtualNode, hostParent, children) if instance.didMount ~= nil then instance:didMount() @@ -289,7 +289,7 @@ function Component:__update(updatedElement, updatedState) local renderResult = virtualNode.instance:render() internalData.setStateBlockedReason = nil - reconciler.updateVirtualNodeWithElements(virtualNode, virtualNode.hostParent, renderResult) + reconciler.updateVirtualNodeChildrenFromElements(virtualNode, virtualNode.hostParent, renderResult) if self.didUpdate ~= nil then self:didUpdate(oldProps, oldState) diff --git a/lib/ElementUtils.lua b/lib/ElementUtils.lua index 1385335a..44e50abd 100644 --- a/lib/ElementUtils.lua +++ b/lib/ElementUtils.lua @@ -64,7 +64,11 @@ function ElementUtils.iterateElements(elementOrElements) return noop end - error("Invalid children") + if regularType == "table" then + return pairs(elementOrElements) + end + + error("Invalid elements") end --[[ @@ -93,6 +97,10 @@ function ElementUtils.getElementByKey(elements, hostKey) return elements.elements[hostKey] end + if typeof(elements) == "table" then + return elements[hostKey] + end + error("Invalid elements") end diff --git a/lib/RobloxRenderer.lua b/lib/RobloxRenderer.lua index 35fa1130..a6acb88a 100644 --- a/lib/RobloxRenderer.lua +++ b/lib/RobloxRenderer.lua @@ -141,7 +141,6 @@ function RobloxRenderer.mountHostNode(reconciler, virtualNode) local children = element.props[Children] - -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result reconciler.updateVirtualNodeChildren(virtualNode, virtualNode.hostObject, children) instance.Parent = hostParent @@ -200,7 +199,6 @@ function RobloxRenderer.updateHostNode(reconciler, virtualNode, newElement) end end - -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result reconciler.updateVirtualNodeChildren(virtualNode, virtualNode.hostObject, newElement.props[Children]) -- Resume event firing now that everything's updated successfully diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index 01bd44ba..dd91e90e 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -4,22 +4,6 @@ local ElementUtils = require(script.Parent.ElementUtils) local Children = require(script.Parent.PropMarkers.Children) local Logging = require(script.Parent.Logging) -local ElementTable = {} - -function ElementTable.getElementByKey(elements, key) - return elements[key] -end - -function ElementTable.iterateElements(elements) - if elements ~= nil then - return pairs(elements) - else - return function() - return nil - end - end -end - --[[ The reconciler is the mechanism in Roact that constructs the virtual tree that later gets turned into concrete objects by the renderer. @@ -65,14 +49,14 @@ local function createReconciler(renderer) Utility to update the children of a virtual node based on zero or more updated children given as elements. ]] - local function updateVirtualNodeChildrenInternal(virtualNode, hostParent, newChildElements, utils) + local function updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) assert(Type.of(virtualNode) == Type.VirtualNode) local removeKeys = {} -- Changed or removed children for childKey, childNode in pairs(virtualNode.children) do - local newElement = utils.getElementByKey(newChildElements, childKey) + local newElement = ElementUtils.getElementByKey(newChildElements, childKey) local newNode = updateVirtualNode(childNode, newElement) if newNode ~= nil then @@ -87,7 +71,7 @@ local function createReconciler(renderer) end -- Added children - for childKey, newElement in utils.iterateElements(newChildElements) do + for childKey, newElement in ElementUtils.iterateElements(newChildElements) do local concreteKey = childKey if childKey == ElementUtils.UseParentKey then concreteKey = virtualNode.hostKey @@ -105,20 +89,17 @@ local function createReconciler(renderer) end end - --[[ - Utility to update the children of a virtual node based on zero or more - updated children given as elements. - ]] - local function updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) - updateVirtualNodeChildrenInternal(virtualNode, hostParent, newChildElements, ElementTable) - end - - --[[ - Utility to update the children of a virtual node based on zero or more - updated children given as an element or a fragment - ]] - local function updateVirtualNodeWithElements(virtualNode, hostParent, newChildElements) - updateVirtualNodeChildrenInternal(virtualNode, hostParent, newChildElements, ElementUtils) + local function updateVirtualNodeChildrenFromElements(virtualNode, hostParent, newChildElements) + if newChildElements == nil + or typeof(newChildElements) == "boolean" + or Type.of(newChildElements) == Type.Element + or Type.of(newChildElements) == Type.Fragment + then + updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) + else + -- TODO: Better error message + error(("%s\n%s"):format("Component returned invalid children:", virtualNode.currentElement.source), 0) + end end --[[ @@ -149,7 +130,7 @@ local function createReconciler(renderer) local function updateFunctionVirtualNode(virtualNode, newElement) local children = newElement.component(newElement.props) - updateVirtualNodeWithElements(virtualNode, virtualNode.hostParent, children) + updateVirtualNodeChildrenFromElements(virtualNode, virtualNode.hostParent, children) return virtualNode end @@ -172,7 +153,6 @@ local function createReconciler(renderer) local children = newElement.props[Children] - -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result updateVirtualNodeChildren(virtualNode, targetHostParent, children) return virtualNode @@ -270,7 +250,7 @@ local function createReconciler(renderer) local children = element.component(element.props) - updateVirtualNodeWithElements(virtualNode, virtualNode.hostParent, children) + updateVirtualNodeChildrenFromElements(virtualNode, virtualNode.hostParent, children) end local function mountPortalVirtualNode(virtualNode) @@ -281,7 +261,6 @@ local function createReconciler(renderer) assert(renderer.isHostObject(targetHostParent)) - -- FIXME: Calling updateVirtualNodeChildren with a 'children' value that is not a render result updateVirtualNodeChildren(virtualNode, targetHostParent, children) end @@ -388,7 +367,7 @@ local function createReconciler(renderer) unmountVirtualNode = unmountVirtualNode, updateVirtualNode = updateVirtualNode, updateVirtualNodeChildren = updateVirtualNodeChildren, - updateVirtualNodeWithElements = updateVirtualNodeWithElements, + updateVirtualNodeChildrenFromElements = updateVirtualNodeChildrenFromElements, } return reconciler diff --git a/lib/createReconciler.spec.lua b/lib/createReconciler.spec.lua index 528ef04a..df49fbd0 100644 --- a/lib/createReconciler.spec.lua +++ b/lib/createReconciler.spec.lua @@ -50,6 +50,34 @@ return function() end) end) + describe("elements", function() + it("should throw errors when attempting to mount invalid element values", function() + local stringElement = "Hello" + local numberElement = 1 + local functionElement = function() end + local tableElement = {} + + local hostParent = nil + local key = "Some Key" + + expect(function() + noopReconciler.mountVirtualNode(stringElement, hostParent, key) + end).to.throw() + + expect(function() + noopReconciler.mountVirtualNode(numberElement, hostParent, key) + end).to.throw() + + expect(function() + noopReconciler.mountVirtualNode(functionElement, hostParent, key) + end).to.throw() + + expect(function() + noopReconciler.mountVirtualNode(tableElement, hostParent, key) + end).to.throw() + end) + end) + describe("Host components", function() it("should invoke the renderer to mount host nodes", function() local mountHostNode = createSpy(NoopRenderer.mountHostNode) From 2684878ebc4932e4ec2d33f057182a5e2f67573b Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Fri, 30 Nov 2018 17:58:18 -0800 Subject: [PATCH 08/11] Fix test to be more clearly targeted --- lib/createReconciler.lua | 5 ++++- lib/createReconciler.spec.lua | 25 +++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index dd91e90e..afb18249 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -98,7 +98,10 @@ local function createReconciler(renderer) updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) else -- TODO: Better error message - error(("%s\n%s"):format("Component returned invalid children:", virtualNode.currentElement.source), 0) + error(("%s\n%s"):format( + "Component returned invalid children:", + virtualNode.currentElement.source or "" + ), 0) end end diff --git a/lib/createReconciler.spec.lua b/lib/createReconciler.spec.lua index df49fbd0..782e677b 100644 --- a/lib/createReconciler.spec.lua +++ b/lib/createReconciler.spec.lua @@ -52,28 +52,37 @@ return function() describe("elements", function() it("should throw errors when attempting to mount invalid element values", function() - local stringElement = "Hello" - local numberElement = 1 - local functionElement = function() end - local tableElement = {} + -- These function components return values with incorrect types + local returnsString = function() + return "Hello" + end + local returnsNumber = function() + return 1 + end + local returnsFunction = function() + return function() end + end + local returnsTable = function() + return {} + end local hostParent = nil local key = "Some Key" expect(function() - noopReconciler.mountVirtualNode(stringElement, hostParent, key) + noopReconciler.mountVirtualNode(createElement(returnsString), hostParent, key) end).to.throw() expect(function() - noopReconciler.mountVirtualNode(numberElement, hostParent, key) + noopReconciler.mountVirtualNode(createElement(returnsNumber), hostParent, key) end).to.throw() expect(function() - noopReconciler.mountVirtualNode(functionElement, hostParent, key) + noopReconciler.mountVirtualNode(createElement(returnsFunction), hostParent, key) end).to.throw() expect(function() - noopReconciler.mountVirtualNode(tableElement, hostParent, key) + noopReconciler.mountVirtualNode(createElement(returnsTable), hostParent, key) end).to.throw() end) end) From a181f3d2def6521725fecee372cfe7439b1d4b69 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Mon, 3 Dec 2018 13:46:28 -0800 Subject: [PATCH 09/11] Use Logging, make tests more detailed --- lib/ElementUtils.spec.lua | 33 ++++++++++++++++++++++++++++++++- lib/createReconciler.lua | 2 +- lib/createReconciler.spec.lua | 4 ++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/ElementUtils.spec.lua b/lib/ElementUtils.spec.lua index a909fa7d..e35438f1 100644 --- a/lib/ElementUtils.spec.lua +++ b/lib/ElementUtils.spec.lua @@ -38,6 +38,27 @@ return function() expect(seenChildren[children.elements.b]).to.equal("b") end) + it("should iterate over tables", function() + local children = { + a = createElement("TextLabel"), + b = createElement("TextLabel"), + } + + local seenChildren = {} + local count = 0 + + for key, child in ElementUtils.iterateElements(children) do + expect(typeof(key)).to.equal("string") + expect(Type.of(child)).to.equal(Type.Element) + seenChildren[child] = key + count = count + 1 + end + + expect(count).to.equal(2) + expect(seenChildren[children.a]).to.equal("a") + expect(seenChildren[children.b]).to.equal("b") + end) + it("should return a zero-element iterator for booleans", function() local booleanIterator = ElementUtils.iterateElements(false) expect(booleanIterator()).to.equal(nil) @@ -76,7 +97,7 @@ return function() end) end) - it("should return the corresponding element", function() + it("should return the corresponding element from a fragment", function() local children = createFragment({ a = createElement("TextLabel"), b = createElement("TextLabel"), @@ -86,6 +107,16 @@ return function() expect(ElementUtils.getElementByKey(children, "b")).to.equal(children.elements.b) end) + it("should return the corresponding element from a table", function() + local children = { + a = createElement("TextLabel"), + b = createElement("TextLabel"), + } + + expect(ElementUtils.getElementByKey(children, "a")).to.equal(children.a) + expect(ElementUtils.getElementByKey(children, "b")).to.equal(children.b) + end) + it("should return nil if the key does not exist", function() local children = createFragment({}) diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index afb18249..2b3f9bd6 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -98,7 +98,7 @@ local function createReconciler(renderer) updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) else -- TODO: Better error message - error(("%s\n%s"):format( + Logging.error(("%s\n%s"):format( "Component returned invalid children:", virtualNode.currentElement.source or "" ), 0) diff --git a/lib/createReconciler.spec.lua b/lib/createReconciler.spec.lua index 782e677b..c35fe5a1 100644 --- a/lib/createReconciler.spec.lua +++ b/lib/createReconciler.spec.lua @@ -50,8 +50,8 @@ return function() end) end) - describe("elements", function() - it("should throw errors when attempting to mount invalid element values", function() + describe("elements and fragments", function() + it("should throw errors when attempting to mount invalid elements", function() -- These function components return values with incorrect types local returnsString = function() return "Hello" From 0a427c6e93d7b117ea03d186862511535d24dde7 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Mon, 3 Dec 2018 14:24:20 -0800 Subject: [PATCH 10/11] Just a little bit more renaming --- lib/Component.lua | 6 +++--- lib/createReconciler.lua | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/Component.lua b/lib/Component.lua index 90f9dec2..f89d2407 100644 --- a/lib/Component.lua +++ b/lib/Component.lua @@ -185,10 +185,10 @@ function Component:__mount(reconciler, virtualNode) virtualNode.context = instance._context internalData.setStateBlockedReason = "render" - local children = instance:render() + local renderResult = instance:render() internalData.setStateBlockedReason = nil - reconciler.updateVirtualNodeChildrenFromElements(virtualNode, hostParent, children) + reconciler.updateVirtualNodeWithRenderResult(virtualNode, hostParent, renderResult) if instance.didMount ~= nil then instance:didMount() @@ -289,7 +289,7 @@ function Component:__update(updatedElement, updatedState) local renderResult = virtualNode.instance:render() internalData.setStateBlockedReason = nil - reconciler.updateVirtualNodeChildrenFromElements(virtualNode, virtualNode.hostParent, renderResult) + reconciler.updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, renderResult) if self.didUpdate ~= nil then self:didUpdate(oldProps, oldState) diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index 2b3f9bd6..389e3247 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -89,13 +89,13 @@ local function createReconciler(renderer) end end - local function updateVirtualNodeChildrenFromElements(virtualNode, hostParent, newChildElements) - if newChildElements == nil - or typeof(newChildElements) == "boolean" - or Type.of(newChildElements) == Type.Element - or Type.of(newChildElements) == Type.Fragment + local function updateVirtualNodeWithRenderResult(virtualNode, hostParent, renderResult) + if renderResult == nil + or typeof(renderResult) == "boolean" + or Type.of(renderResult) == Type.Element + or Type.of(renderResult) == Type.Fragment then - updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) + updateVirtualNodeChildren(virtualNode, hostParent, renderResult) else -- TODO: Better error message Logging.error(("%s\n%s"):format( @@ -133,7 +133,7 @@ local function createReconciler(renderer) local function updateFunctionVirtualNode(virtualNode, newElement) local children = newElement.component(newElement.props) - updateVirtualNodeChildrenFromElements(virtualNode, virtualNode.hostParent, children) + updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, children) return virtualNode end @@ -253,7 +253,7 @@ local function createReconciler(renderer) local children = element.component(element.props) - updateVirtualNodeChildrenFromElements(virtualNode, virtualNode.hostParent, children) + updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, children) end local function mountPortalVirtualNode(virtualNode) @@ -370,7 +370,7 @@ local function createReconciler(renderer) unmountVirtualNode = unmountVirtualNode, updateVirtualNode = updateVirtualNode, updateVirtualNodeChildren = updateVirtualNodeChildren, - updateVirtualNodeChildrenFromElements = updateVirtualNodeChildrenFromElements, + updateVirtualNodeWithRenderResult = updateVirtualNodeWithRenderResult, } return reconciler From 799e6545c81e28d99f52301e4e08829754931696 Mon Sep 17 00:00:00 2001 From: Paul Doyle Date: Thu, 6 Dec 2018 15:54:56 -0800 Subject: [PATCH 11/11] Wrapped versions of both updating with children and updating with render results --- lib/RobloxRenderer.lua | 4 ++-- lib/createReconciler.lua | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/RobloxRenderer.lua b/lib/RobloxRenderer.lua index a6acb88a..47f71a25 100644 --- a/lib/RobloxRenderer.lua +++ b/lib/RobloxRenderer.lua @@ -141,7 +141,7 @@ function RobloxRenderer.mountHostNode(reconciler, virtualNode) local children = element.props[Children] - reconciler.updateVirtualNodeChildren(virtualNode, virtualNode.hostObject, children) + reconciler.updateVirtualNodeWithChildren(virtualNode, virtualNode.hostObject, children) instance.Parent = hostParent virtualNode.hostObject = instance @@ -199,7 +199,7 @@ function RobloxRenderer.updateHostNode(reconciler, virtualNode, newElement) end end - reconciler.updateVirtualNodeChildren(virtualNode, virtualNode.hostObject, newElement.props[Children]) + reconciler.updateVirtualNodeWithChildren(virtualNode, virtualNode.hostObject, newElement.props[Children]) -- Resume event firing now that everything's updated successfully if virtualNode.eventManager ~= nil then diff --git a/lib/createReconciler.lua b/lib/createReconciler.lua index 389e3247..b6822f03 100644 --- a/lib/createReconciler.lua +++ b/lib/createReconciler.lua @@ -49,7 +49,7 @@ local function createReconciler(renderer) Utility to update the children of a virtual node based on zero or more updated children given as elements. ]] - local function updateVirtualNodeChildren(virtualNode, hostParent, newChildElements) + local function updateChildren(virtualNode, hostParent, newChildElements) assert(Type.of(virtualNode) == Type.VirtualNode) local removeKeys = {} @@ -89,13 +89,17 @@ local function createReconciler(renderer) end end + local function updateVirtualNodeWithChildren(virtualNode, hostParent, newChildElements) + updateChildren(virtualNode, hostParent, newChildElements) + end + local function updateVirtualNodeWithRenderResult(virtualNode, hostParent, renderResult) if renderResult == nil or typeof(renderResult) == "boolean" or Type.of(renderResult) == Type.Element or Type.of(renderResult) == Type.Fragment then - updateVirtualNodeChildren(virtualNode, hostParent, renderResult) + updateChildren(virtualNode, hostParent, renderResult) else -- TODO: Better error message Logging.error(("%s\n%s"):format( @@ -156,7 +160,7 @@ local function createReconciler(renderer) local children = newElement.props[Children] - updateVirtualNodeChildren(virtualNode, targetHostParent, children) + updateVirtualNodeWithChildren(virtualNode, targetHostParent, children) return virtualNode end @@ -264,7 +268,7 @@ local function createReconciler(renderer) assert(renderer.isHostObject(targetHostParent)) - updateVirtualNodeChildren(virtualNode, targetHostParent, children) + updateVirtualNodeWithChildren(virtualNode, targetHostParent, children) end --[[ @@ -369,7 +373,7 @@ local function createReconciler(renderer) mountVirtualNode = mountVirtualNode, unmountVirtualNode = unmountVirtualNode, updateVirtualNode = updateVirtualNode, - updateVirtualNodeChildren = updateVirtualNodeChildren, + updateVirtualNodeWithChildren = updateVirtualNodeWithChildren, updateVirtualNodeWithRenderResult = updateVirtualNodeWithRenderResult, }