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
Merged
Show file tree
Hide file tree
Changes from 3 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 scripts/fiber/tests-passing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,7 @@ src/renderers/dom/shared/__tests__/inputValueTracking-test.js
* should track value and return true when updating untracked instance
* should return tracker from node
* should stop tracking
* does not crash for nodes with custom value property

src/renderers/dom/shared/__tests__/quoteAttributeValueForBrowser-test.js
* should escape boolean to string
Expand Down
31 changes: 27 additions & 4 deletions src/renderers/dom/shared/__tests__/inputValueTracking-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
'use strict';

var React = require('react');
var ReactTestUtils = require('react-dom/test-utils');
var ReactDOM = require('react-dom');
// TODO: can we express this test with only public API?
var inputValueTracking = require('inputValueTracking');

Expand Down Expand Up @@ -135,9 +135,8 @@ describe('inputValueTracking', () => {
});

it('should return tracker from node', () => {
var node = ReactTestUtils.renderIntoDocument(
<input type="text" defaultValue="foo" />,
);
var div = document.createElement('div');
var node = ReactDOM.render(<input type="text" defaultValue="foo" />, div);
var tracker = inputValueTracking._getTrackerFromNode(node);
expect(tracker.getValue()).toEqual('foo');
});
Expand All @@ -153,4 +152,28 @@ describe('inputValueTracking', () => {

expect(input.hasOwnProperty('value')).toBe(false);
});

it('does not crash for nodes with custom value property', () => {
// https://github.com/facebook/react/issues/10196
try {
var originalCreateElement = document.createElement;
document.createElement = function() {
var node = originalCreateElement.apply(this, arguments);
Object.defineProperty(node, 'value', {
get() {},
set() {},
});
return node;
};
var div = document.createElement('div');
// 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.

// Unmount
ReactDOM.unmountComponentAtNode(div);
} finally {
document.createElement = originalCreateElement;
}
});
});
27 changes: 18 additions & 9 deletions src/renderers/dom/shared/inputValueTracking.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,32 +128,41 @@ var inputValueTracking = {
inst._wrapperState.valueTracker = trackValueOnNode(node, inst);
},

// TODO: in practice "subject" can currently be Stack instance,
// Fiber, or DOM node. This is hard to understand. We should either
// make it accept only DOM nodes, or only Fiber/Stack instances.
updateValueIfChanged(subject: SubjectWithWrapperState | Fiber) {
if (!subject) {
return false;
}
var tracker = getTracker(subject);

var isNode = (subject: any).nodeType === ELEMENT_NODE;
var tracker = getTracker(subject);
if (!tracker) {
if (typeof (subject: any).tag === 'number') {
if (isNode) {
// DOM node
inputValueTracking.trackNode((subject: any));
} else if (typeof (subject: any).tag === 'number') {
// Fiber
inputValueTracking.trackNode((subject: any).stateNode);
} else {
// Stack
inputValueTracking.track((subject: any));
}
return true;
}

var lastValue = tracker.getValue();

var node = subject;

// TODO: remove check when the Stack renderer is retired
if ((subject: any).nodeType !== ELEMENT_NODE) {
var node;
if (isNode) {
// 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.

}

var lastValue = tracker.getValue();
var nextValue = getValueFromNode(node);

if (nextValue !== lastValue) {
tracker.setValue(nextValue);
return true;
Expand Down