Noticed a weird behavior. I use react-datepicker and react-select. When the select is opened, if i click on the datepicker, the select field closes and the datepicker opens as expected.
But when the datepicker is open, if i try to click on the select, the select opens and the datepicker closes BUT almost instantly the select closes and datepicker opens again.
I'm also seeing this....not sure which one to blame here but at least seems to be related to these lines:
https://github.com/Hacker0x01/react-datepicker/blob/master/src/datepicker.jsx#L156-L157
Without that deferFocusInput() works for me. Not really sure why that is needed or what it is actually achieving so can't really comment on how to fix it. If someone has some pointers of fixing this I could try submitting PR. Didn't yet check anything on react-select side so there might be also something.
Added example repository for reproducing this:
https://github.com/tume/select-datepicker-conflict
Just react-datepicker example + empty react-select component. You can see the weird behaviour when opening calendar and then trying to open the select component.
I was digging this a bit deeper and my understanding now is that onBlur is being called before onClickOutside which means the calendar is still open when onBlur is called. And this seems to happen because react-select listens to mouseDown event and is calling focus in that handler and by my speculation is then calling onBlur in react-datepicker.
I'm not so familiar with all these events and their correct calling order so would appreciate if someone can point out that should this be fixed in this component or is this more to do with react-select ?
@tume The lines u referenced here: https://github.com/Hacker0x01/react-datepicker/blob/master/src/datepicker.jsx#L156-L157 , removing them fixes the issue?
@gilbertKaradja I was testing by calling this.props.onBlur(event) always so skipping that deferFocusInput().
And that fixed the flickering?
Yeah in my testing but it's not really a proper fix.
Sure, but have you noticed any side-effects? I haven't had time to properly investigate this, might do that tomorrow
If I recall correctly, removing those lines would cause the input to not refocus after the calendar is clicked.
@rafeememon: If that's all it does I am willing to sacrifice that feature to get rid of the flicker-bug
Unfortunately that'd be breaking behavior that I'm not sure others would be willing to sacrifice =)
@rafeememon: I did't mean remove the functionality entirely. I will employ the "fix" until I or someone else finds a proper solution
Any update on this?
@gilbertKaradja, nope, any help creating a proper fix would be great
Same problem
+1
One way that worked for me:
<DatePicker onChange={ this.onDateValueChange } selected={this.state.date} ref="picker" onClickOutside={this.clickOutside}/>;
And clickOutside method:
clickOutside() {
this.refs.picker.cancelFocusInput();
this.refs.picker.setOpen(false);
}
I made a proof of concept PR that removes onclickoutside almost entirely and instead uses blur to hide the calendar component. I personally believe onclickoutside is inherently flawed and that any other component on the page should be able to call stopPropagation without breaking another.
https://github.com/malectro/react-datepicker/pull/1
Unfortunately this changes some basic behavior of react-datepicker and breaks 4 tests (mostly related to the year and month dropdowns and how it responds to blur events that aren't due to clicks). But I don't feel these changes to be degradations.
Thoughts? There's a lot more work to do fixing up the tests and possibly removing onclickoutside entirely, but I wanted to get feedback before proceeding.
Workaround, that works for me until the issue will be fixed:
import DatePicker from "react-datepicker";
class CustomDatePicker extends DatePicker {
constructor(props) {
super(props);
}
deferFocusInput = () => {
this.cancelFocusInput();
}
}
export default CustomDatePicker;
and then you could use CustomDatePicker instead of DatePicker
From what I can tell, the problem is that the handleBlur method is blindly refocusing the input when it is blurred, if the calendar was open. When react-select is clicked, it is focused (and the menu opens) but the calendar input immediately "steals" focus back again.
I can't work out what the actual purpose of calling the deferFocusInput method is (or actually trace how the component detects that something else has been focused, for example tabbing through the inputs in the example) so if someone can shed light on this I'd be happy to help work out how both components can play nicely together.
@JedWatson AFAICT, the purpose of that is to get the focus to "stay" on the datepicker when clicking within the calendar without closing it. (For example, clicking to the next month arrow shouldn't change the focus, much like clicking the scroll arrows in react-select doesn't change the focus.)
It's been a long time since I looked at it, but I think react-select may be preventing handleCalendarClickOutside from getting called, so the calendar refocuses itself as if the click was within the calendar.
There may also be some weirdness going on because after calling this.setState, this.state will still have the old value until the next update. I'm not sure the state is guaranteed to be updated between handleCalendarClickOutside and handleBlur, though clicking on a regular input does seem to work. (And we do want to trigger an update/render when the open state is changed.)
@aij thanks for the quick reply!
With that in mind, it makes sense that stopping propagation in react-select could prevent the document event handler from detecting the click in handleCalendarClickOutside. Would be interesting to see if that prop is fired when you click in react-select, if anybody's got an example handy.
Although the issue is most obvious when react-datepicker is used in a form with react-select, I don't think you can make any guarantees that there wouldn't be other event handlers on the page that could receive focus and stop the event from bubbling - hopefully it's possible to use a different solution in react-datepicker, rather than preventing react-select from cancelling events. As a rule, I'd expect that if I get an event it's "mine", and messing with it shouldn't have side-effects for the component that previously had focus.
My suggestion would be to use a different technique (not async refocus on blur) to maintain focus when a user clicks in the calendar - catch events in the calendar's outermost container, and refocus then, or something like that. I used a similar technique in react-select to handle clicks in the menu, because we need to keep the input "browser focused".
(sorry if I sounds opinionated here, not my intention)
I had the same problem using react-final-form BTW and I fixed it calling the input.onBlur callback inside the onClickOutside function of DatePicker.
@Alexey-Lukin's patch will let input loose focus if you click inside calendar.
Here I've made some improvements to keep the focus:
import RDP from "react-datepicker";
class ReactDatepicker extends RDP {
constructor(props) {
super(props);
let calendar = null;
Object.defineProperty(this, 'calendar', {
get: () => calendar,
set: v => {
if (!calendar && v) {
v.componentNode.addEventListener('click', this.ownDeferFocusInput);
}
calendar = v;
},
});
}
ownDeferFocusInput = () => {
this.cancelFocusInput();
this.inputFocusTimeout = setTimeout(this.setFocus, 1);
};
deferFocusInput = () => {};
}
export default ReactDatepicker;
This works for me
class DateTimeTest extends React.Component {
constructor(props) {
super(props);
this.OnOutsideClick = this.OnOutsideClick.bind(this);
this.pickerRef = null;
}
OnOutsideClick() {
this.pickerRef.cancelFocusInput();
}
render() {
return (
<ReactDatePicker
ref={(node) => { this.pickerRef = node; }}
onClickOutside={this.OnOutsideClick}
/>
);
}
}
One way that worked for me:
<DatePicker onChange={ this.onDateValueChange } selected={this.state.date} ref="picker" onClickOutside={this.clickOutside}/>;And
clickOutsidemethod:clickOutside() { this.refs.picker.cancelFocusInput(); this.refs.picker.setOpen(false); }
Thanks. it's work for me
One way that worked for me:
And clickOutside method:
clickOutside() {
this.refs.picker.cancelFocusInput();
this.refs.picker.setOpen(false);
}
Thanks Alot. it's work for me
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Is this solved or are you open to PRs? I looked through the release descriptions on npm but couldn't see a resolution referenced.
Most helpful comment
Workaround, that works for me until the issue will be fixed:
and then you could use CustomDatePicker instead of DatePicker