Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release syntheticEvent.target on the destructor #5840

Merged
merged 1 commit into from
Jan 16, 2016

Conversation

koba04
Copy link
Contributor

@koba04 koba04 commented Jan 13, 2016

Currently, SyntheticEvent isn't releasing the target property when it called destructor.
This PR makes the target property null.

Please refer to the following fiddle.

https://jsfiddle.net/koba04/9mdse5h9/

@@ -500,6 +500,7 @@ function makeSimulator(eventType) {
fakeNativeEvent,
node
);
event.persist();
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems unrelated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@syranide
No. Some tests depend on this issue's behavior.

https://github.com/facebook/react/blob/master/src/test/__tests__/ReactTestUtils-test.js#L433-L465

I prefer that the SyntheticEvent in TestUtils.Simulate isn't released after the event callback has been invoked.
Should I separate a commit or create an another PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know enough about testing and how this is used, but if it affects behavior for the event received by the callbacks then this seems wrong.

Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't do this. It would make Simulate.* event handlers behave differently than they would in the browser where events aren't automatically persisted (eg a handler calls something with the event in a timeout would end up working in tests but fail in real life).

We should fix that and any other tests, something like this.

var foo = {
  onChange = function(e) {
    e.persist();
  }
}
spyOn(foo, 'onChange').andCallThrough();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I removed this line and fixed the failed tests!

@quantizor
Copy link
Contributor

This constitutes a potential memory leak, yeah?

@jimfb
Copy link
Contributor

jimfb commented Jan 13, 2016

@yaycmyk I don't think it constitutes a memory leak. Persisting the event just means it won't be returned to the synthetic event pool, which should be fine. It will still get garbage collected when all the references are dropped.

@quantizor
Copy link
Contributor

@jimfb I guess I mean if the event target is a node that has been deleted since the event was persisted, the node reference won't be able to be GC'd until it is nulled out or the event is reused as you said. Kind of a leak...

@zpao
Copy link
Member

zpao commented Jan 13, 2016

Persisting is generally an anti-pattern so I'm not too concerned about references there. Regardless, when you call persist(), that instance gets marked and it won't be returned to the pool. As a result, that SyntheticEvent will just get GCed like anything else and a good GC should release values as well if they aren't referenced anywhere else.

I think the current state of the world without this changes means we have a memory leak (albeit fixed size, so not an exponentially growing leaking)

@@ -160,6 +160,7 @@ assign(SyntheticEvent.prototype, {
this.dispatchConfig = null;
this._targetInst = null;
this.nativeEvent = null;
this.target = null;
Copy link
Member

Choose a reason for hiding this comment

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

We probably want to do this for currentTarget as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Ah yea, good call. Really target should be in that interface too but I get why it's not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@zpao

Really target should be in that interface too

I think so. DOM3 Event interface has target attribute.
SyntheticEvent has all DOM3 Event interface attributes except target.

https://www.w3.org/TR/DOM-Level-3-Events/#event-interfaces

Why it's not.

Isn't this enough?

diff --git a/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js b/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
index f9d0206..48ba4e8 100644
--- a/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
+++ b/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
@@ -23,6 +23,7 @@ var warning = require('warning');
  */
 var EventInterface = {
   type: null,
+  target: null,
   // currentTarget is set when dispatching; no use in copying it here
   currentTarget: emptyFunction.thatReturnsNull,
   eventPhase: null,
@@ -55,16 +56,17 @@ var EventInterface = {
 function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
   this.dispatchConfig = dispatchConfig;
   this._targetInst = targetInst;
-
   this.nativeEvent = nativeEvent;
-  this.target = nativeEventTarget;
-  this.currentTarget = nativeEventTarget;

   var Interface = this.constructor.Interface;
   for (var propName in Interface) {
     if (!Interface.hasOwnProperty(propName)) {
       continue;
     }
+    if (propName === 'target') {
+      this.target = nativeEventTarget;
+      continue;
+    }
     var normalize = Interface[propName];
     if (normalize) {
       this[propName] = normalize(nativeEvent);
@@ -160,7 +162,6 @@ assign(SyntheticEvent.prototype, {
     this.dispatchConfig = null;
     this._targetInst = null;
     this.nativeEvent = null;
-    this.target = null;
   },

 });

Copy link
Member

Choose a reason for hiding this comment

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

Yea, that looks right. Seems like we shouldn't have been setting currentTarget in the c'tor anyway.

I get why it's not

I just meant that it feels awkward to have to check for === 'target' every time. We could probably move it into the else case below where you have it and then drop the continue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@zpao Thanks! I updated this.

@koba04 koba04 force-pushed the release-event-target-on-destructor branch from fbf5159 to 9d2e2c7 Compare January 14, 2016 01:17
@facebook-github-bot
Copy link

@koba04 updated the pull request.

@koba04 koba04 force-pushed the release-event-target-on-destructor branch from 9d2e2c7 to be0551d Compare January 14, 2016 06:13
@facebook-github-bot
Copy link

@koba04 updated the pull request.

@syranide syranide mentioned this pull request Jan 14, 2016
@zpao
Copy link
Member

zpao commented Jan 16, 2016

Looks great, thanks!

zpao added a commit that referenced this pull request Jan 16, 2016
Release syntheticEvent.target on the destructor
@zpao zpao merged commit 422b4e1 into facebook:master Jan 16, 2016
@koba04
Copy link
Contributor Author

koba04 commented Jan 18, 2016

Thanks!

@koba04 koba04 deleted the release-event-target-on-destructor branch January 18, 2016 02:18
@zpao zpao modified the milestones: 0.14.x, 0.14.7 Jan 26, 2016
@koba04 koba04 mentioned this pull request Apr 5, 2016
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants