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

react-datepicker interferes with react-select #730

Closed
gilbertkaradja opened this issue Feb 3, 2017 · 30 comments
Closed

react-datepicker interferes with react-select #730

gilbertkaradja opened this issue Feb 3, 2017 · 30 comments
Labels

Comments

@gilbertkaradja
Copy link

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.

@tume
Copy link

tume commented Feb 6, 2017

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.

@tume
Copy link

tume commented Feb 7, 2017

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 ?

@gilbertkaradja
Copy link
Author

gilbertkaradja commented Feb 7, 2017

@tume The lines u referenced here: https://github.com/Hacker0x01/react-datepicker/blob/master/src/datepicker.jsx#L156-L157 , removing them fixes the issue?

@tume
Copy link

tume commented Feb 7, 2017

@gilbertkaradja I was testing by calling this.props.onBlur(event) always so skipping that deferFocusInput().

@gilbertkaradja
Copy link
Author

And that fixed the flickering?

@tume
Copy link

tume commented Feb 7, 2017

Yeah in my testing but it's not really a proper fix.

@gilbertkaradja
Copy link
Author

Sure, but have you noticed any side-effects? I haven't had time to properly investigate this, might do that tomorrow

@rafeememon
Copy link
Contributor

If I recall correctly, removing those lines would cause the input to not refocus after the calendar is clicked.

@gilbertkaradja
Copy link
Author

@rafeememon: If that's all it does I am willing to sacrifice that feature to get rid of the flicker-bug

@rafeememon
Copy link
Contributor

Unfortunately that'd be breaking behavior that I'm not sure others would be willing to sacrifice =)

@gilbertkaradja
Copy link
Author

@rafeememon: I did't mean remove the functionality entirely. I will employ the "fix" until I or someone else finds a proper solution

@gilbertkaradja
Copy link
Author

Any update on this?

@rafeememon
Copy link
Contributor

@gilbertkaradja, nope, any help creating a proper fix would be great

@ibcWillJust42
Copy link

Same problem

@kevinrue
Copy link

kevinrue commented Mar 7, 2017

+1

@shkumbin
Copy link

shkumbin commented Mar 27, 2017

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);
}

@swbullis
Copy link

@malectro
Copy link
Contributor

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.

malectro#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.

@Alexey-Lukin
Copy link

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

@JedWatson
Copy link

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.

@aij
Copy link
Contributor

aij commented Sep 12, 2017

@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.)

@JedWatson
Copy link

@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)

@rafeememon rafeememon changed the title react-datepicker interferes with react select react-datepicker interferes with react-select Dec 14, 2017
@horaciosystem
Copy link

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.

@primetwig
Copy link

@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;

@jacknam8315
Copy link

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}
        />
    );

  }
} 

@akbar-pyt
Copy link

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);
}

Thanks. it's work for me

@praveenkgoswami
Copy link

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);
}
Thanks Alot. it's work for me

@stale
Copy link

stale bot commented Aug 30, 2019

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.

@stale stale bot added the wontfix label Aug 30, 2019
@stale stale bot closed this as completed Sep 13, 2019
@samthomson
Copy link

Is this solved or are you open to PRs? I looked through the release descriptions on npm but couldn't see a resolution referenced.

@rkulinski
Copy link

Nothing of the above seems to be working now using newest versions. Although I think this is react-select fault.
JedWatson/react-select#5050

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests