-
Notifications
You must be signed in to change notification settings - Fork 46.8k
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
Use an ordered property list for inputs, fix some Chrome number input issues #7474
Conversation
It would appear that a couple of server-side rendering tests fail:
I apologize for not catching this, there's a specific server-side rendered test that I added the However these other tests relate to updates, which I was not aware was possible for server side rendering. Is this the correct server-side rendered behavior for these tests? Either way, it looks like this test path isn't activated when running |
f182a2f
to
bce5dff
Compare
It’s not hard, just a bit long and currently requires changing the source code (yea, not ideal). |
@gaearon Cool. I'll make that a part of my flow. As for the other question. I'm simply wrong. The tests caught a bug. If markup is server-rendered, I removed React's chance chance to directly assign value to the input before it updated. I've pushed a fix. Passes locally. We'll see what Travis says :) |
30fa07c
to
f5e5324
Compare
for (propKey in nextProps) { | ||
var nextProp = nextProps[propKey]; | ||
var lastProp = | ||
if (ordered != null && ordered.indexOf(propKey) >= 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.
How does this perform for an <input />
that may have numerous props, compared to the current implementation? An indexOf
check for every prop in nextProps
seems potentially expensive.
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.
I'll run a benchmark. In an earlier version of this PR I stored the properties in an array and an object to eliminate indexOf calls, but I removed it because it added extra parts and primitive benchmarks of indexOf
weren't compelling (especially with the general performance improvement by eliminating work in ReactDOMInput).
So let's find out!
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.
Formalized my original benchmark and put it on the internet:
https://github.com/nhunzaker/react-ordered-props-bench
I'm happy to accept any profiling tips here. I'm a heavy user of React and benefit from accuracy. I want to get this right.
For your specific question, I added a bunch of properties (custom attributes, if that matters) to some controlled inputs:
http://natehunzaker.com/react-ordered-props-bench/heavy-props.html
CONTROL 1628 (+= 2.299877893626147)
EXPERIMENT 1750 (+= 1.870748777520246)
Fastest is EXPERIMENT
Though the noise makes it hard to say it's faster, I do not think it is slower.
Unfortunately, I noticed that there seems to be a performance regression with uncontrolled inputs:
http://natehunzaker.com/react-ordered-props-bench/uncontrolled.html
CONTROL 5307 (+= 3.5737538615198474)
EXPERIMENT 4475 (+= 2.2288121746511043)
Fastest is CONTROL
Unclear why I didn't catch this before. I'm also not sure specifically why there's a regression. This PR includes fixes to "decontrol" uncontrolled inputs (so that they don't reassign themselves and screw up number inputs). I do not know yet if the performance degradation relates to that, or the ordered properties approach generally. I'm looking in to that now.
One other interesting secondary finding is that the _handleChange
binding always gets applied to uncontrolled inputs, even if there is no change handler:
https://github.com/facebook/react/blob/master/src/renderers/dom/client/wrappers/ReactDOMInput.js#L79
Best I can tell this is waste. Is there a good reason for _handleChange
to fire on uncontrolled inputs? If not, we could kill some overhead here.
f5e5324
to
f58f614
Compare
f58f614
to
ae22ecf
Compare
Hi, we use controlled inputs in a lot of our forms and face this issue. @nhunzaker thanks a ton for taking the pain of trying to solve this. May I know the current state of this PR, I would like to be of some help if needed. |
Hey @kaushik94 Check out #7359. This PR is an alternative fix (that also eliminates the need for a lot of other input fixes), but #7359 has helped me to identify that it doesn't address every concern raised by the React team (specifically setting the value attribute as a controlled input updates). There's a pretty long conversation there, but I believe I've documented every approach I have tried within the comments. The gist of it is, I've solved every use case except the following: Controlled inputs should (according to the team) assign a value attribute as a controlled input updates. This is to avoid issues with To be honest, I'm sort of tapped out at this point and waiting some feedback from the team (see #7359 (comment)). I'm still putting in time as I can to explore solutions, but I would love fresh thoughts. In the short term, I think something has to be done to at least make number inputs useable. I'll submit another PR shortly that solves these issues for uncontrolled inputs in a non-breaking way. No reason to block progress while we sort out what to do about controlled inputs. |
@@ -209,6 +229,54 @@ var HTMLDOMPropertyConfig = { | |||
}, | |||
DOMPropertyNames: { | |||
}, | |||
DOMMutationMethods: { | |||
value: function(node, next) { |
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.
value
is shared by <progress>
(and some other tag?), so this seems like a no-go. A progress with null value must remove the attribute.
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.
Ya. Took care of that in #7359.
Hmm.. This PR is creating some extra noise for the input issue. Should I close this for now and resubmit after that settles?
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.
Ah heck, I'll just bring it over while I'm thinking about it. That way things are at least up to date. Thanks for the prod!
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.
Added, and I brought over the <progress>
test case from #7359 too. Though it re-introduces the input[type="number"]
decimal place loss issue. Either way both PRs are synced.
I don't feel awesome about this value
mutation method, but with all of the mutations in one place, it's way easier to reason about the [type="number"]
issues on this branch. Curious if I'll get any inspiration shifting back here.
ae22ecf
to
8e70fa2
Compare
Server rendered markup never has `value` assigned as a property, so it never detaches from defaultValue. There must be a postMount hook on ReactDOMInput to assign value.
8e70fa2
to
4e5ac47
Compare
I'm going to close this out to cut down on clutter and avoid confusion with #7359. |
Discussed in #7428. I'm finally to a point where I feel good enough about this code and I thought I'd make it easier to comment on it.
This is a reimplementation of #7359 that additionally eliminates much of the work done inside of ReactDOMInput to handle quirks associated with
defaultValue
,value
,defaultChecked
, andchecked
.It does this by ensuring a specific order for property assignment on the
input
element. This is implemented withinDOMProperty
andHTMLDOMPropertyConfig
, which may allow for other elements to eventually take advantage of it (should they need it).Performance changes
Since they do less work, controlled inputs are slightly faster, while non-ordered HTML tags have no observable performance regression (in my ability to test, anyway).
Fixes from the original PR
This is a major deviation from my solution in #7359, but it includes the same input fixes. These are enumerated in #7253 (comment). Basically, since the mutation method for
value
first checks to see if the property is loosely the same, it eliminates many edge cases with controlled number inputs, like:3.
to3
parseFloat
orparseInt
won't "expand" values, like magically replace3e1
with30
.Other fixes
Some fixes are no longer necessary inside of
ReactDOMInput
:type
andstep
beforevalue
for IE11defaultValue
/value
for inputs to ensure date inputs display properly on Android and iOS.defaultChecked
does not influencechecked
in Chrome 50.xThis is a big change. I don't expect it to be merged easily. If there is any additional documentation I can provide, or benchmarks you would like to see, I am happy to produce them.