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

Fix crash on master introduced during the radio bugfix #10207

Merged
merged 4 commits into from
Jul 18, 2017

Conversation

gaearon
Copy link
Collaborator

@gaearon gaearon commented Jul 18, 2017

Fixes #10196.
See individual commits for descriptions of how this got broken, and why this fix works.

This is a minimal fix to unbreak master.

Follow-up items (not part of this PR) that @jquense and I will look into:

  • Remove lazy tracker path from updateValueIfChanged. It isn’t useful, and makes code more complicated. It is easier if we can assume that tracker is always exists while component is mounted.

  • Simplify the wild polymorphism. This method accepts a fiber, a Stack instance, and a DOM node. Stack will be gone soon, so we should look into either making it always accept a DOM node, or maybe making it always accept a fiber.

  • Related: reduce the abstraction in naming. I think Fix uncontrolled radios #10156 made things slightly more confusing by calling argument a “subject” and we lost track of what it means. Let’s be explicit and precise about what exactly it is, and try to use Flow with less anys here.

The failure happens because trackValueOnNode() can exit early if it detects an existing descriptor on node, or if it sees a "broken" Safari descriptor (which is how we encountered this bug in the wild).

As a result, the tracker field was not set, and subsequent updateValueIfChanged() call went into the branch that initializes the tracker lazily. That branch has a bug in Fiber mode where it passes the wrong type.

We did not see this issue before because that branch is relatively hard to hit (you have to either run it in Safari or define a custom DOM value descriptor which is what I did in the test).

In the future, we will likely remove the lazy branch altogether since if we bailed out of setting up the tracker once, we will likely bail out every time. But for now I'm just focused on a minimal fix to unbreak master.
// Mount
var node = ReactDOM.render(<input type="text" />, div);
// Update
ReactDOM.render(<input type="text" />, div);
Copy link
Contributor

Choose a reason for hiding this comment

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

do we also want to exercise the change plugin route? e.g. trigger a change event on the input.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

How to do this?

Copy link
Contributor

Choose a reason for hiding this comment

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

SimulateNative.change(node) should work, if the event triggered it passed through the function. It isn't specifically gonna catch a failure in Fiber but it covers the all paths through updateValueIfChanged.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That was a great idea, thanks.

This makes it go through the Fiber argument code path.
@gaearon
Copy link
Collaborator Author

gaearon commented Jul 18, 2017

Addressed.

// DOM node
node = subject;
} else {
// Fiber and Stack
node = ReactDOMComponentTree.getNodeFromInstance(subject);
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to be clear for myself here, getNodeFromInstance works on Fibers?

Copy link
Contributor

@aweary aweary Jul 18, 2017

Choose a reason for hiding this comment

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

Copy link
Contributor

@jquense jquense left a comment

Choose a reason for hiding this comment

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

🎉 thanks for fixing it :) I'm already working on a simplification pr, per your notes above

@gaearon gaearon merged commit 9043ad6 into facebook:master Jul 18, 2017
@gaearon
Copy link
Collaborator Author

gaearon commented Jul 18, 2017

I think the biggest reason it’s so complicated is because we don’t just support Stack and Fiber, but also that they hold wrapper state in different places. So passing a DOM node is not “enough” because only Fiber keeps wrapper state on the DOM node.

I wonder if the easiest solution to this is just to move wrapper state to DOM node in Stack. So that it works the same way as Fiber, and we can always pass the DOM node.

@gaearon gaearon deleted the repro-bug branch July 18, 2017 15:32
@gaearon
Copy link
Collaborator Author

gaearon commented Jul 18, 2017

If it’s too hard to simplify now we can also wait until Stack is gone. It shouldn’t take long.

@jquense
Copy link
Contributor

jquense commented Jul 18, 2017

I wonder if the easiest solution to this is just to move wrapper state to DOM node in Stack.

that is interesting...Then you in both cases you could use getNodeFromInstance to retrieve the thing with wrapper state, and avoid having to deal with instances in most cases.

@jquense
Copy link
Contributor

jquense commented Jul 18, 2017

We could just store the value tracker on the DOM node, and leave the wrapperState out of it. Originally (way back) I believe I did that, but it made less sense from a correctness standpoint. In this case tho it leads to a significant simplification...

@gaearon
Copy link
Collaborator Author

gaearon commented Jul 18, 2017

Let’s try that?

jquense added a commit to jquense/react that referenced this pull request Jul 18, 2017
This moves the storage of the input value tracker to the DOM node
instead of the wrapperState. This makes it easier to support both Stack
and Fiber without out a lot of ugly type casting and logic branches.

related: facebook#10207
jquense added a commit that referenced this pull request Jul 19, 2017
* Move input valueTracker to DOM nodes

This moves the storage of the input value tracker to the DOM node
instead of the wrapperState. This makes it easier to support both Stack
and Fiber without out a lot of ugly type casting and logic branches.

related: #10207

* run prettier

* remove instance accepting methods

* rm unused trst method

* address feedback

* fix naming

* fix lint
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.

Uncontrolled radio fix is breaking master
4 participants