Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Disconnect events during unmount #282

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

## [1.3.1](https://github.com/Roblox/roact/releases/tag/v1.3.1) (November 19th, 2020)
* Added component name to property validation error message ([#275](https://github.com/Roblox/roact/pull/275))
* Fixed an issue where events were called while unmounting a host element ([#282](https://github.com/Roblox/roact/pull/282))

## [1.3.0](https://github.com/Roblox/roact/releases/tag/v1.3.0) (May 5th, 2020)
* Added Contexts, which enables easy handling of items that are provided and consumed throughout the tree.
Expand Down
4 changes: 4 additions & 0 deletions src/RobloxRenderer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ function RobloxRenderer.unmountHostNode(reconciler, virtualNode)

applyRef(element.props[Ref], nil)

if virtualNode.eventManager ~= nil then
virtualNode.eventManager:disconnectAll()
end

for _, childNode in pairs(virtualNode.children) do
reconciler.unmountVirtualNode(childNode)
end
Expand Down
19 changes: 19 additions & 0 deletions src/RobloxRenderer.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,25 @@ return function()
expect(spyRef.callCount).to.equal(2)
spyRef:assertCalledWith(nil)
end)

itSKIP("should not process events when unmounting", function()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this still be skipped? If it's not currently passing, should it get to passing before being merged?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what I should do with this, because the test passes when it's running in studio, but not using Lemur, because it does not simulate the ChildRemoved event. So I don't know if I should implement the feature in Lemur or if there is another way to test this!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZoteTheMighty how should we handle this?

local parent = Instance.new("Folder")
local key = "Some Key"

local element = createElement("Frame", {
[Event.ChildRemoved] = function(instance)
error("this callback should not be called during unmount")
end,
}, {
Child = createElement("Frame"),
})

local node = reconciler.createVirtualNode(element, parent, key)

RobloxRenderer.mountHostNode(reconciler, node)

RobloxRenderer.unmountHostNode(reconciler, node)
end)
end)

describe("Portals", function()
Expand Down
13 changes: 13 additions & 0 deletions src/SingleEventManager.lua
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,17 @@ function SingleEventManager:resume()
self._suspendedEventQueue = {}
end

function SingleEventManager:disconnectAll()
self._status = EventStatus.Disabled

for eventKey, connection in pairs(self._connections) do
connection:Disconnect()
self._listeners[eventKey] = nil
end

self._listeners = {}
self._connections = {}
self._suspendedEventQueue = {}
end

return SingleEventManager
18 changes: 18 additions & 0 deletions src/SingleEventManager.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,22 @@ return function()
end).to.throw()
end)
end)

describe("disconnectAll", function()
it("should not invoke events fired during suspension but disconnected before resumption", function()
local instance = Instance.new("BindableEvent")
local manager = SingleEventManager.new(instance)
local eventSpy = createSpy()

manager:connectEvent("Event", eventSpy.value)
manager:suspend()

instance:Fire(1)

manager:disconnectAll()

manager:resume()
expect(eventSpy.callCount).to.equal(0)
end)
end)
end