diff --git a/modules/react-roblox/src/client/__tests__/ReactRobloxBindings.roblox.spec.luau b/modules/react-roblox/src/client/__tests__/ReactRobloxBindings.roblox.spec.luau index 99b4119e..ade810ef 100644 --- a/modules/react-roblox/src/client/__tests__/ReactRobloxBindings.roblox.spec.luau +++ b/modules/react-roblox/src/client/__tests__/ReactRobloxBindings.roblox.spec.luau @@ -98,6 +98,34 @@ it("should not return the same root twice", function() jestExpect(reactRobloxRoot).never.toBe(reactRobloxRoot2) end) +it("should unsubscribe from bindings when unmounted", function() + local counterBinding, setCounter = React.createBinding(0) + local renderCount = 0 + local function Component() + return React.createElement("TextLabel", { + Text = counterBinding:map(function(counter) + renderCount += 1 + return tostring(counter) + end), + }) + end + ReactRoblox.act(function() + reactRobloxRoot:render(React.createElement(Component)) + end) + jestExpect(renderCount).toBe(1) + ReactRoblox.act(function() + reactRobloxRoot:render(React.createElement(Component)) + end) + jestExpect(renderCount).toBe(2) + setCounter(1) + jestExpect(renderCount).toBe(3) + ReactRoblox.act(function() + reactRobloxRoot:unmount() + end) + setCounter(2) + jestExpect(renderCount).toBe(3) +end) + describe("useBinding hook", function() it("returns the same binding object each time", function() local captureBinding = jest.fn() diff --git a/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau b/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau index 226e6c53..015ccc42 100644 --- a/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau +++ b/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau @@ -306,15 +306,24 @@ local function updateProperties( end end +local function cleanupBindings(domElement: HostInstance) + local instanceBindings = instanceToBindings[domElement] + if instanceBindings ~= nil then + for _, disconnectBinding in instanceBindings do + disconnectBinding() + end + instanceToBindings[domElement] = nil + end +end + -- ROBLOX deviation: Clear out references to components when they unmount so we -- avoid leaking memory when they're removed local function cleanupHostComponent(domElement: HostInstance) if instanceToEventManager[domElement] ~= nil then instanceToEventManager[domElement] = nil end - if instanceToBindings[domElement] ~= nil then - instanceToBindings[domElement] = nil - end + + cleanupBindings(domElement) -- ROBLOX https://jira.rbx.com/browse/LUAFDN-718: Tables are somehow ending up -- in this function that expects Instances. In that case, we won't be able to @@ -328,9 +337,8 @@ local function cleanupHostComponent(domElement: HostInstance) if instanceToEventManager[descElement] ~= nil then instanceToEventManager[descElement] = nil end - if instanceToBindings[descElement] ~= nil then - instanceToBindings[descElement] = nil - end + + cleanupBindings(descElement) removeAllTags(domElement) end end