-
Notifications
You must be signed in to change notification settings - Fork 48.9k
Eliminate unnecessary numeric equality checks #11751
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
Eliminate unnecessary numeric equality checks #11751
Conversation
This commit changes the way numeric equality for number inputs works such that it compares against `input.valueAsNumber`. This eliminates quite a bit of branching around numeric equality.
Can you explain why the original code was written this way, and why it's no longer necessary despite comments saying something about IE9? |
) { | ||
if (props.type === 'number') { | ||
// eslint-disable-next-line | ||
if (node.valueAsNumber !== value && node.value != value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I wonder if I even need to check valueAsNumber. Everything works as expected in IE9, which does not support this API.
) { | ||
if (props.type === 'number') { | ||
// eslint-disable-next-line | ||
if (node.value != value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be too good to be true, but this checks out in every browser. Unit tests also confirm it. Have I missed something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth checking git blame to see why this was introduced.
) { | ||
if (props.type === 'number') { | ||
// eslint-disable-next-line | ||
if (node.value != value || (value === 0 && node.value === '')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we didn't cover ""
to 0
in our unit tests. Added.
Identifying all of the edge cases was a long road, and we didn't have all of the test fixtures and unit tests to cover edge cases in type coercion. It was the result of adding additional branches to cover different cases.
IE9 does not support I think the gist of this is just that we do not need to parse the value as a number. We can make the following reductions (or at least this is my logic so we can try to poke holes in it): if (props.type === 'number') {
// Simulate `input.valueAsNumber`. IE9 does not support it
var valueAsNumber = parseFloat(node.value) || 0;
if (
// eslint-disable-next-line
value != valueAsNumber ||
// eslint-disable-next-line
(value == valueAsNumber && node.value != value)
) {
if (props.type === 'number') {
// Simulate `input.valueAsNumber`. IE9 does not support it
var valueAsNumber = parseFloat(node.value) || 0;
if (
// eslint-disable-next-line
value != valueAsNumber && node.value != value
) { To the best of my ability to test, if (props.type === 'number') {
// eslint-disable-next-line
if (value != node.value && node.value != value) { This is the same check! So finally we get to: if (props.type === 'number') {
// eslint-disable-next-line
if (value != node.value) { The edge case here is that if (props.type === 'number') {
// eslint-disable-next-line
if (if (node.value != value || (value === 0 && node.value === '')) { |
@@ -248,6 +248,23 @@ describe('ReactDOMInput', () => { | |||
} | |||
}); | |||
|
|||
it('performs as state change from "" to 0', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"performs a state change"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it in 2ba3fe8
(value == valueAsNumber && node.value != value) | ||
) { | ||
if (props.type === 'number') { | ||
// eslint-disable-next-line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we update this so it's only disabling the coercion rule?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea. I also moved the loose check after the value === 0
guard to avoid some extra DOM access. 3cdde77
) { | ||
if (props.type === 'number') { | ||
// eslint-disable-next-line | ||
if (node.value != value || (value === 0 && node.value === '')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before this change the 0
/ ""
coercion would occur for any type of input. Now it only applies to number inputs. Any chance that breaks something weird?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not from my ability to test. The strict equality check covers it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does look like parseFloat
and ==
are logically equivalent in this situation. I can't think of any situation where it fails, so assuming all the fixtures pass in the supported browsers this LGTM
I'll check this into #11741 in a bit and that one should be good to go. |
…1741) * Ensure value and defaultValue do not assign functions and symbols * Eliminate assignProperty method from ReactDOMInput * Restore original placement of defaultValue reservedProp * Reduce branching. Make assignment more consistent * Control for warnings in symbol/function tests * Add boolean to readOnly assignments * Tweak the tests * Invalid value attributes should convert to an empty string * Revert ChangeEventPlugin update. See #11746 * Format * Replace shouldSetAttribute call with value specific type check DOMProperty.shouldSetAttribute runs a few other checks that aren't appropriate for determining if a value or defaultValue should be assigned on an input. This commit replaces that call with an input specific check. * Remove unused import * Eliminate unnecessary numeric equality checks (#11751) * Eliminate unnecessary numeric equality checks This commit changes the way numeric equality for number inputs works such that it compares against `input.valueAsNumber`. This eliminates quite a bit of branching around numeric equality. * There is no need to compare valueAsNumber * Add test cases for empty string to 0. * Avoid implicit boolean JSX props * Split up numeric equality test to isolate eslint disable command * Fix typo in ReactDOMInput test * Add todos * Update the attribute table
This commit changes the way numeric equality for number inputs works such that it compares loose equality for numbers. This seems sufficient, and eliminates quite a bit of branching around numeric equality.
This additionally addresses Flow type issues on #11741, and it is based off that branch
Test plan:
Tested in: