I have a simple search bar (controlled text input) with autocomplete.
When I type text very quickly, I do get onKeyDown events, but every now and then, the corresponding onChange event goes missing. For example, if I type "foobar", the input field may end up showing "foobr".
This happens only on IE (tested with IE11 on Windows 7), and I have not seen this on any other browser.
Here's a fiddle showing the issue: https://jsfiddle.net/zjfbow3w/3/ and a log:
keyDown: f
keyDown: o
change: f
render: f
change: fo
render: fo
render: fo
keyDown: o
keyDown: b
change: foo
render: foo
change: foob
render: foob
keyDown: a
render: foob
keyDown: r
change: foobr
render: foobr
render: foobr
Note that onKeyDown handler sees that "a", but not the onChange handler.
The fiddle includes a timer (which in real code triggers an AJAX call), which is somehow important for this issue -- if I remove the timer, I cannot reproduce this issue any more.
The React version in the fiddle is 15.1.0 (but this happens on 0.14.3, too).
It's really weird, I don't see anything wrong with your code... this should not happen. So this should indeed be a bug. I can't really wrap my head around how or why this behavior is happening, will be interesting to see what's causing it.
Really interesting.
cc @jquense I'm sure you'll love this ;)
interesting... @pasieronen could you possibly try using React master and see if it's still a problem? i also wonder if it happens on older ie..hmm
IE11 affected.
IE9, IE10 and Edge unaffected.
It looks to me like IE11 is setting the value as soon as the user types a keystroke, and enqueuing the event handler. As a result, the timeout callback calls setState, which causes a reconciliation, which resets the input value, which causes the onChange
handler to read the wrong value from the DOM node.
Notice that if you replace the onTimeout
logic with this.setState({unused: 1, value: this.refs.input.value})
, the input starts working as expected again.
To me, this looks like a bug in IE (setting the value should happen immediately prior to calling onChange) rather than a bug in React.
To me, this looks like a bug in IE (setting the value should happen immediately prior to calling onChange) rather than a bug in React.
@jimfb onChange
is polyfilled so that seems like an odd statement? Or am I missing something?
Anyway, after some debugging I don't really feel a lot smarter. However, it seems the root of the problem may be related to the keyup/keydown/etc handling of ChangeEventPlugin in a sense. To accurately reproduce the problem, hold down a key and then press a different one without letting go of the first and you have reproduced the problem, which may further hint at the underlying problem. It's also worth noting that in that instance, logging onInput
will actually output the correct value (before being overwritten by React) and if you update the state with it will turn out correct. On another note, anyone know why we aren't using onInput
for IE11?
@jimfb onChange is polyfilled so that seems like an odd statement? Or am I missing something?
Sure, I should have more correctly said: The native event that causes our onChange
handler function to fire.
On another note, anyone know why we aren't using onInput for IE11?
cc @jquense
the most recent reason is that it fires too often and inconsistently. changing a placeholder for instance fires onInput, re-using the polyfill was a quick way to fix it though it seems to probably have cause more bugs than fixed (browsers!). master listens for both onChange and onInput and dedupe em, which is (hopefully) a more correct solution
what I've learned is that the polyfill doesn't scale super well to ie11 which being the first "good" IE seems to break a bunch of MS-isms that make it work in ie8-9. Also there are probably a bunch of these bugs in old IE too but didn't notice them because of lower market share?
Is there any temporal workaround for that, that could be applied in the app?
@smashercosmo
I think you could update the value of your controlled component during your timeout/setState call, as I mentioned earlier...
Notice that if you replace the onTimeout logic with this.setState({unused: 1, value: this.refs.input.value}), the input starts working as expected again.
Alternatively, you could restructure your code to avoid calling setState in an asynchronous/timeout callback. I think if you call setState() only in the event handler, things will work without an issue, since the problem is a race condition between the characters typed and the setState calls.
I realize neither of those are particularly good workarounds.
I would also recommend filing a bug with the microsoft/IE11 team, since this looks like an IE bug to me. Setting the node's value and firing the event handlers should be synchronous. Other timers/events (like setTimeout) should not be injected between those two operations. This assumes that my understanding of the issue is correct. If you can reproduce the bug without React (should be possible, just do whatever React is doing, probably just setting the node's value directly in the setTimeout call would be sufficient), that will likely help the IE team narrow down the issue.
On our end, I'm not sure there is much we could do about this problem. At first glance it appears to be a browser bug, though I'd love to be proven wrong (it is conceivable this is a React bug). Assuming it is a browser bug, we might be able to mitigate the problem, but we may not be able to eliminate it entirely. In years of React, you're first person to run into this, so it is probably not the highest item on our todo list, but I do understand that it is annoying/frustrating to run into things like this. Unfortunately, browser bugs are often difficult for us to fix on our end.
On our end, I'm not sure there is much we could do about this problem.
@jimfb Considering that onInput
does report the new value correctly intuitively this should be entirely fixable unless there's something else blocking.
Alternatively, you could restructure your code to avoid calling setState in an asynchronous/timeout callback. I think if you call setState() only in the event handler, things will work without an issue, since the problem is a race condition between the characters typed and the setState calls.
While true, that doesn't take into consideration any unrelated asynchronous updates that may happen elsewhere in the application, which would cause it to update and rerender. Outside of your control.
I would also recommend filing a bug with the microsoft/IE11 team, since this looks like an IE bug to me. Setting the node's value and firing the event handlers should be synchronous. Other timers/events (like setTimeout) should not be injected between those two operations.
I'm not convinced IE11 is really at fault here, as far as I can tell we have a hacky workaround for reimplementing oninput which may rely on very specific browser behavior of keydown/keyup/etc (keydown fires _before_ value is updated). Determining the exact fault or adding the native oninput (which seems to work) into the mix may solve the problem. I don't have any faith in IE11 shipping a fix for this any time soon (or ever, and everyone also updating to it) and we're bound to support it for a very long time.
@jimfb @syranide The thing is that react 0.14.8 handles this correctly. I have two demos with the exact code and different versions of react. One is buggy in IE11 and one is not.
buggy demo, non-buggy demo
@smashercosmo
The React version in the fiddle is 15.1.0 (but this happens on 0.14.3, too).
@smashercosmo
The thing is that react 0.14.8 handles this correctly. I have two demos with the exact code and different versions of react. One is buggy in IE11 and one is not.
Oh, weird, ok, so this was broken in 0.14.3, fixed in 0.14.8, and broken again in 15.1.0, is that correct?
I just tried on my master fiddle (http://jsfiddle.net/s2ot6w4z/) and it seemed to be fine. @smashercosmo, can you verify that it is fixed in the fiddle? http://jsfiddle.net/s2ot6w4z/
@jimfb I confirm. Latest react doesn't have this speedtyping issue. Tested both demos (yours and mine).
master _does_ listen for onInput; if IE is correctly reporting there this should be fixed. like I said above it just turns out that the polyfill we have for onInput actually has a lot of edge cases no one noticed :P (and it's assumptions don't quite work on ie11)
Ok, we close out bugs when they're fixed in master. This fix will be part of a future release.
I know this bug has been closed by changes to master, but it's still affecting the 15 branch.
I don't think that @syranide is correct about this being an IE11 bug, he correctly points out it seems to be caused by making asynchronous updates to state, but I think the cause of the bug is that react uses onpropertychange
to generate change events in IE11, which is asynchronous!
Consequently any state changes triggered in response to a change event in IE11 are racy, and may cause input to be lost.
I would assume that since the fix was on master in mid-June that it went out with 15.2.0
or 15.2.1
? I ask because I'm still seeing this problem on 15.2.1, on a large redux-form
form on IE11 machines of sufficient slowness. If it didn't go out yet, is there any estimate on when it will go out?
@HankMcCoy master is v16.0.0 so any v15 wouldn't include them
Our company works in the construction industry and therefore has a lot of clients on old machines with IE11 filling out massive forms, so this is a pretty substantial problem for us. Do you know where in the codebase the fix was applied? We're seriously considering temporarily forking in order to cherry-pick the fix for this, if that is even remotely feasible. And thanks for the ludicrously fast reply, @jquense!
I forget the PR number, but you can npm install react@next that might do it for you.
Sadly react@next is 15.3, so still no fix. :/
Is this the PR in question: https://github.com/facebook/react/pull/5746? I cherry picked that change onto the 15-stable
branch locally and rebuilt my app with the forked version but am still seeing the problematic behavior. Are there perhaps other changes on master
that contribute to fixing this that I missed?
I believe that's the only one. I do think that there is a releases v16 alpha perhaps.
We encounter the same issue here; just wondering when v16.0.0 is due to be released?
We are seeing the same issue. It's a pretty big deal for us since our users are on IE11 for enterprise policy reasons. This bug renders our app almost unusable to them.
Any chance of getting a fix back-ported to v15 before v16 comes out? Is there a workaround we can apply to our own code in the meantime?
Would just like to back up @PenguinOfThunder. Back-porting this fix to the 15 release would be _immensely_ helpful. As it is this is screwing us over pretty royally with customers stuck on IE11 (of which there are, unfortunately, many).
For now, the best I can suggest is to build from master yourself, or grab a nightly master build here: http://react.zpao.com/builds/master/latest/. Obviously they are not guaranteed to be stable.
(Please make your own copy, don’t link to @zpao’s website 😉 )
@gaearon, good to know, thanks. Is there a resource anywhere outlining, be it however unreliably, what kind of breaking changes are likely to land in 16 (and thus we'd be likely to run into on master
)? I looked through the last couple React meeting notes but couldn't find anything.
gonna just leave this here again https://github.com/facebook/react/pull/5746#issuecomment-224249973
One last comment on this one: I'm honestly pretty confused why y'all aren't taking the time to back port this to React 15. About 28% of our web app users are on IE11 and probably the most crucial aspect of our entire app is our complex, dynamic forms. For those 28% of users they currently receive an _extremely_ unpleasant, occasionally _unusable_ user experience. Obviously, most apps will have a substantially lower percentage of IE11 users (we're in the construction space, thus the high count), but even more general browser share estimates place it at somewhere in the vicinity of 15% usage. I know Facebook runs React master in production, so I'm guessing you don't feel the pain that this particular bug causes, so I just want to make sure you're all aware: the pain is substantial. :/
If you are curious, total IE usage is 6.3% on fb and IE11 is 5.6%.
@HankMcCoy I'm not FB and I'm not defending it, but personally I've started switching away from the current uncontrolled/controlled behavior and instead rely on controlled behavior when unfocused but when it's focused it becomes uncontrolled (but still emits onChange naturally). IMHO it generally has a far more desirable behavior as applying formatting to controlled inputs is "broken" anyway (not a bug), this way you can apply formatting and it will show up harmlessly when you unfocus. It also avoids issues with IMEs and so on.
I have to go with @HankMcCoy. At my company we serve both SaaS and On-Premise and while IE11 usage is pretty low on SaaS it is close to 100% for the On-Premise customers. In the past react always helped out so much, because it abstracted away all those quirks and helped us develop fast. However this bug is now being reported by most of the On-Premise customers.
So it would be highly appreciated if this fix could get into a 15.x release as fast as possible or at least, we would need a clue when 16.0 is ready, because simply telling customers to wait till it's fixed is really putting us in a hard place.
I thought I would comment on my progress so far and hopefully help some people out that come here looking for answers.
The workaround: The inconvenient truth here is that this bug affects developers like me, who run applications in a strictly regulated enterprise environment. I was lucky, because I could ask my users to temporarily use a different browser and I was able to convince our IT department to allow those users to run Firefox. Other developers like @HankMcCoy and @frontendphil aren't quite as fortunate.
But if you can convince your users to switch, by all means try it. Nobody should have to run IE11 in this day and age. Unfortunately that does not reflect reality for 99% of people on this thread.
The fix: I cloned React from the master branch, and I was able (with a bit of effort) to get it to work with my system. I have not found that this bug is not present in React 16, but testing continues to make sure there are no other showstoppers.
If you want to attempt to upgrade, be warned that your mileage is going to vary wildly, depending on how complex your dependency tree is and whether your app (or modules you depend on) are using deprecated features. I had a couple of modules that I had to fork and edit to work with React 16. Others could be replaced with new modules that were maintained better.
The rant: In retrospect this has been a valuable lesson in exactly how fragile the npm module system is. You can't trust semantic versioning to prevent breaking changes and dependency hell is real. Going forward, I sure will be a lot more careful to upgrade modules even by a single patch-level. This is not unique to React either; "semantic versioning" have simply become meaningless at this point and that is not likely to change. The only way to be sure is to test, test, test.
To others that come here looking for a fix in React 15: The fix will (IMO) not be backported to 15 because Facebook has zero motivation to do so. They move fast and break things. There is no reason for them to fix an old version when they run a newer one in production. It is no use banging your head against this brick wall any longer.
If your application is currently broken, try moving to React 16 like @jquense recommended. It works (for now) for me, and you will have to upgrade to 16 eventually anyway. Just make sure you test thoroughly and that you freeze your dependencies once you know it works.
End of rant.
You can't trust semantic versioning to prevent breaking changes and dependency hell is real.
that isn't really true, this bug was introduced on a major bump, not during a patch or feature update. If you were pinned to the major version (the default) you'd have no problems.
Sorry @jquense, I should have been more clear. I did not mean to imply that React in this case violated the semantic versioning "spec". I was commenting on the fragile ecosystem as a whole, not React specifically.
What I was referring to was what happens when I had to update third-party modules that relied on older versions of React. In my case their new versions had support for at least React 15, but were not as strict with their versioning, leading to unexpected breakage. React is fine used in isolation, but it's madness when you start importing third-party components. I chalk this one up as a lesson learned.
I feel you pain, but pain in this particular area is mostly due to browsers still having widely different behaviors in the way that events work. Case in point this bug is a regression caused inadvertently while fixing a bunch of other really old IE event bugs.
Not trying to make an excuse for bugs; React would definitely benefit from having this code covered by actual browser tests, something that is sadly missing right now.
May I ask you when it will be in 15.x or when the version 16 will be released? This issue is a serious problem for us.
I'm facing this issue too, and in my case I ended up replacing onChange with onInput
It's not recommended by React, but at least it works for me
How is it possible? IE11 would have a 🐛 (bug)?
Joke apart, I'm now waiting for React 16.x.
I've tested react 16.0.0-alpha
today and I can still reproduce this issue on IE11. Isn't 16.0.0-alpha
based on the master branch thus having code that fixes this issue? Anyone else experiencing this?
On another note, we tried replacing onChange
with onInput
(which worked great for IE11) as @felixkurniawan did, but that made typing in IE10 stop completely for us.
@JohanBengtsson I did not test my tempory fix on IE10 (yes like everyone I try to step away IE as much as I can 😄) but did you set defaultValue
too?
Here is my TextInput
custom component (= my tempory fix until React 16) working smoothly on IE11 (even with a temporised callback to manage value in redux store without performance cost):
import React, {
Component,
PropTypes
} from 'react';
import shallowCompare from 'react-addons-shallow-compare';
class TextInput extends Component {
constructor(props) {
super(props);
this.state = { stateValue: '' };
this.handlesOnChange = this.handlesOnChange.bind(this);
this.timer = null;
}
componentWillReceiveProps(nextProps) {
const { stateValue } = this.state;
const { value } = nextProps;
if ((value !== stateValue) && stateValue.length === 0) {
this.setState({stateValue: value});
}
}
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}
componentWillUnmount() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
render() {
const {label, id} = this.props;
const { stateValue } = this.state;
return (
<div className="form-group">
<label
className="control-label"
htmlFor={id}>
{label}
</label>
<div>
<input
className="form-control"
id={id}
type="text"
// value={value}
defaultValue={stateValue}
onInput={this.handlesOnChange}
/>
</div>
</div>
);
}
handlesOnChange(event) {
event.preventDefault();
this.setState({stateValue: event.target.value});
this.setTimerBeforeCallback(event.target.value);
}
setTimerBeforeCallback(value) {
const { onChange, delay } = this.props;
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
this.timer = setTimeout(
() => onChange(value),
delay
);
}
}
TextInput.propTypes = {
label: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
delay: PropTypes.number
};
TextInput.defaultProps = {
delay: 200
};
export default TextInput;
@MacKentoch Thanks for the tip! Yeah we've got it working well for IE11 now with a short term work around of changing to onInput
.. but still have no good solution for IE10 and we don't want to have spend too much time on it. Unfortunately we have a lot of customer at universities/hospitals (etc.) where individuals aren't always allowed to upgrade from IE10. But either way... unwillingly we have decided to temporarily drop IE10 support 'til a proper solution in React 16 is out.
I switched to using onInput, this then broke IE10 & IE9. The following disgusting hack is what I have ended up doing until it's fixed, YUCK.
```
import { IE_VERSION } from '../../util/browserDetection';
export default function getInputOnChangeProps(handler) {
return IE_VERSION === 11 ?
{ onInput: handler } :
{ onChange: handler };
}
I'd also want to follow up to my post from August last year. It's now been 7 months of waiting :( Last year I hoped that 16.0 is not too far away. However that assumptions seems to be wrong. If this wouldn't be such a super yucky thing I'd be ok with it but I constantly have to answer the question, why we can't fix that.
@frontendphil maybe note that in #8575 :)
I had the same problem. I solved it by using a similar solution as @MacKentoch suggested (using onInput
combined with setTimeout
). However, in the end all I needed to do was to remove e.preventDefault()
from the onChange method to make it work in IE 11.
@pasieronen It's fixed in React version 15.6.1
@bbarnell Upgrading to 15.6.1
does not fix the issue for me.
@programmist My site is now working without changing any code other than the version:
http://www.greenwoodsc.gov/permits/
Unsubscribe
Sent from ProtonMail mobile
-------- Original Message --------
On Sep 15, 2017, 8:22 PM, Jonathan Ankiewicz wrote:
Where is Microsoft IE developers? Do they not use the internet like the rest of us? Do they not see the constant issues IE has?
I'm running into the same issues in production. I'll try pushing for the React update first
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.
This seems to be broken in React 16.4.1 - I am experiencing this same bug in IE11
I have the same problem with React 16.4.1
File a new issue with details please. Thanks. We don't track closed issues.
Most helpful comment
Would just like to back up @PenguinOfThunder. Back-porting this fix to the 15 release would be _immensely_ helpful. As it is this is screwing us over pretty royally with customers stuck on IE11 (of which there are, unfortunately, many).