facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.2k stars 24.33k forks source link

DatepickerIOS onDateChange reports the wrong date #8169

Closed willmcneilly closed 5 years ago

willmcneilly commented 8 years ago

When using datepickerIOS the onDateChange callback sometimes reports the wrong date. By changing between date and month in quick succession you'll quickly find that the date reported by onDateChange is different from that shown on the datepicker. It's worth noting that the callback does fire correctly, it's just the date it provides is stale.

It's much easier to replicate on device but still possible on the simulator. Here's an example of the problem: datepickerios

The code is straight forward:

class DatePickerBug extends Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date()
    };
  }

  onDateChange(date) {
    this.setState({
      date: date
    });
  }

  render() {
    return (
      <View style={styles.container}>
        <Text>{this.state.date.toString()}</Text>
        <DatePickerIOS
          date={this.state.date}
          mode="date"
          onDateChange={(date) => this.onDateChange(date)}
        />
      </View>
    );
  }
}

We've noticed this issue in both 0.26 and 0.27. It didn't exist in 0.22 but because we upgraded our project in one move from 0.22 to 0.26 I'm unsure which version introduced the bug.

If anyone would like to get up and running with the example I have it here https://github.com/willmcneilly/RNDatepickerIOSBug

vonovak commented 8 years ago

Maybe git binary search could help tracking it down..

ammichael commented 8 years ago

I'm having this problem too. But since I'm using time mode, it's returning the wrong hour. Always 1 hour more than the selected. If I selected 9 AM, it returns me 10 AM..

RN 0.21.0

I'll try do upgrade and will report here later

Couldn't really try. When I updated to RN 0.28 my project exploded in so many ways I've to downgrade to 0.21 and relink all libs. Still having the problem.

wsun commented 8 years ago

I can confirm this bug was introduced in 0.26, as far as I can tell commit 47a470a seems to be the culprit. Haven't had a chance to investigate further.

cc @sebmarkbage

sfrog commented 8 years ago

Hi, I wonder know that whether the RN team notice this bug?

sorenburon commented 8 years ago

I'm also experiencing this issue, using RN 0.29.0.

jgibbons commented 8 years ago

same issue with RN 0.34 RC

ammichael commented 8 years ago

I still have this issue with RN 0.33 (kinda obvious since @jgibbons is reporting issues with RN 0.34 RC).

This is a really big issue :(

tomazahlin commented 8 years ago

Same here. It's hard to really tell what is wrong and I also have problem with output day being for 1 to big... I think it's related to timezone or to some old dates, because it works for some dates and not for the others.

ammichael commented 8 years ago

@tomazahlin I'm using time mode. What I've noticed is that the date is always the previous date you've set. If you set the date for day 2, then 4, then 10, you will end up with day 4.

What is really bugging me is.. Are we the only ones with this problem?! It has been 4 months since the first report. Makes me wonder if the RN team noticed this bug at all =(

tomazahlin commented 8 years ago

@ammichael I have just seen I had a totally another problem, which I solved. My problem was that the DatePickerIOS sometimes gave back a date object where day was 1 to less. I solved it by not doing let date = new Date(year,month,day);, but by doing let date = new Date(); date.setUTCFullYear(year,month,day); Using the provided example on the top it works flawlessly. The date always changes. I also cannot reproduce the problem which you describe, about the previous date being set. Maybe try cloning the Date object before setting it to state? I think if the same object reference is set to state or received by props, the component would not re-render. Just my suggestion what you could try.

JakeRawr commented 8 years ago

@ammichael same issue here. I'm using "date" mode. An example is I scrolled to year 2014, then 1996, then 1992. the output stays at 1996. Same thing happens with month or day. What's weird is, it only happens from every so often.

ammichael commented 8 years ago

@tomazahlin I have tried to clone the Date object, but didn't matter =/

@JakeRawr That is why it is so frustrating! It doesn't happen every time, so you can't find a reliable solution..

geekvijay commented 8 years ago

i am using react-native 0.37.0 and In my case loadFromServer function prints old selected date instead of new picked date.

class DatePickerBug extends Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date()
    };
  }

  loadFromServer() {
    // this prints old picked date instead of new picked date
    console.log(this.state.date);
  }

  onDateChange(date) {
    this.setState({
      date: date
    });
    this.loadFromServer();
  }

  render() {
    return (
      <View style={styles.container}>
        <Text>{this.state.date.toString()}</Text>
        <DatePickerIOS
          date={this.state.date}
          mode="date"
          onDateChange={(date) => this.onDateChange(date)}
        />
      </View>
    );
  }
}
rdagger commented 7 years ago

Same issue here.

Mactub07 commented 7 years ago

see problem on RN-0.42 too

davidpfahler commented 7 years ago

This is still broken. It seems to be a problem on the native side, but I cannot fix the native plugin myself, unfortunately. Someone, please help out.


Update: Did some digging and the date is also wrong (i.e. stuck on the last date) inside RCTDatePicker.m (void)didChange, so the problem is before that function is being invoked.


Update 2: As a workaround, I wrapped a View around the DatePickerIOS component to block pointer-events for 500ms after the onDateChange handler was called. This is not ideal, but prevents the user to see false data.

varungupta85 commented 7 years ago

@davidpfahler Do you mind sharing the code that you have used to wrap a View around the date time picker and block the pointer events?

This is what I have right now. Just wanted to make sure I didn't miss anything

class DatePicker extends Component {
  state = {
    allowPointerEvents: true
  }

  onDateChange = (date) => {
    this.props.onDateChange(date)
    this.setState({
      allowPointerEvents: false
    })
    const timeoutId = setTimeout(() => {
      this.setState({
        allowPointerEvents: true
      })
      clearTimeout(timeoutId)
    }, 500)
  }

  render() {
    return (
      <View pointerEvents={this.state.allowPointerEvents ? 'auto' : 'none'}>
        <DatePickerIOS {...this.props} onDateChange={this.onDateChange} />
      </View>
    );
  }
}

Update May 15, 2017 Users started to complain after I plugged in the above workaround and it is still possible to get false data with the above workaround. One way to get false data even with the above workaround is when the user moves the minutes and when the minutes' carousel is about to stop just move the hour hand slightly such that it doesn't change the hour but it does move a little.

It would be great if somebody with native iOS experience could look into it.

ghuh commented 7 years ago

This is still a bug as of RN 0.44.3

varungupta85 commented 7 years ago

This actually is a bug in the iOS itself. I can easily reproduce this problem in the native Reminders and Calendar app.

dgladkov commented 7 years ago

Spent past couple of days fighting this bug, according to my findings problem is that action for UIControlEventValueChanged does not fire at all in some cases [1], specifically if you turn multiple wheels simultaneously in same direction. This is indeed a bug in iOS itself, not in React Native.

I worked around that problem by making a custom native getter method that returns UIDatePicker's date, thus effectively making datepicker uncontrolled. If anyone is interested in this solution, I can make a PR for this.

[1] https://github.com/facebook/react-native/blob/master/React/Views/RCTDatePicker.m#L34

varungupta85 commented 7 years ago

@dgladkov Very interested. I would be happy to just know what I need to change in the RCTDatePicker.m file to get around this problem and try it myself.

dgladkov commented 7 years ago

@varungupta85 I've released my uncontrolled DatePicker version as a library: https://github.com/dgladkov/react-native-uncontrolled-date-picker-ios

I've tried to add getDate method to RCTDatePicker and submit a PR, but current implementation aggressively updates date on each change event, so my approach will not work unless I change current behavior.

varungupta85 commented 7 years ago

Thanks @dgladkov. I will try it out.

JolieNguyen commented 7 years ago

This is how I fix it: onDateChange(date){ var _date = new Date(date); _date.setMinutes( _date.getMinutes() - _date.getTimezoneOffset()); this.setState({birthday: _date}); }

Hope it useful!

antoinerousseau commented 7 years ago

I'm having a weird bug: the date picker reports a wrong date only if my phone's timezone is set to "auto", but not if I manually set it (to the same TZ...)

I'm in Paris TZ (UTC+2). iPhone SE, iOS 11, RN 0.49.

SudoPlz commented 7 years ago

Still a problem in 0.49, how can we let the RN team know about this? It seems to be lost in the endless issue graveyard.

Here's a link to product pains, in case that makes any difference.

Please vote for it if you want that fixed.

https://react-native.canny.io/feature-requests/p/datepickerios-wrong-date-or-time

ammichael commented 7 years ago

Is there any reliable way to get Date on RN? Any other component or something like that, at least? 😕

My app really relies on dates, and this problem is happening for some users (which is a deal breaker for them).

It is weird how a problem like this still exists more than 1 year later from the first report.

dgladkov commented 7 years ago

This turned out to be a subtle iOS bug which I was able to reproduce even in some native iOS apps. Please check my previous comments for a solution that worked for me.

Unfortunately, that solution has clunky API as it requires calling a getter on a ref, so I don't think it is a viable replacement for the current implementation. Other than that, I don't think there's anything RN can do besides documenting the issue.

shazvi commented 7 years ago

I had this same issue as well. In the end, what worked for me was to include the timeZoneOffsetInMinutes attribute to the DatePickerIOS component:

  <DatePickerIOS
      date = {this.state.date}
      mode = "time"
      onDateChange = {this.onDateChange} 
      timeZoneOffsetInMinutes={(new Date()).getTimezoneOffset()*-1}/>

Make sure to multiply timezone offset by -1

ghost commented 6 years ago

If this helps anyone else, you need to check the state of the date within a callback.

_updateState(){
    console.log(this.state.date);
}

onDateChange(date){
    this.setState({date}, () => this._updateState() );
}
stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Maybe the issue has been fixed in a recent release, or perhaps it is not affecting a lot of people. If you think this issue should definitely remain open, please let us know why. Thank you for your contributions.

damir-sirola commented 6 years ago

The issue is not fixed, the problem still occurs

abarisic86 commented 6 years ago

I upgraded project from @willmcneilly to latest stable versions of React & React Native. The bug is still there.

"react": "^16.2.0", "react-native": "^0.52.2"

berkcoker commented 6 years ago

Yeap I can confirm that the bug is still there.

"react": "^16.2.0", "react-native": "^0.52.2"

jasperkuperus commented 6 years ago

Also seeing this on recent React Native versions.

Packages: (wanted => installed)
  react: 16.0.0 => 16.0.0
  react-native: 0.51.0 => 0.51.0

This comment describes very nicely how to reproduce this: https://github.com/facebook/react-native/issues/4787#issuecomment-233582448

SudoPlz commented 6 years ago

react-native-modal-datetime-picker now supports custom picker components. I've resolved this by using the following code: https://github.com/mmazzarolo/react-native-modal-datetime-picker/pull/115#issue-156586891 and it works great.

jasperkuperus commented 6 years ago

https://github.com/dgladkov/react-native-uncontrolled-date-picker-ios works great for me. Would be awesome to add this API in the original DatePickerIOS API, is there anyway to get this under the attention of the React Native team? This issue has been there for as early as December 2015...

janziemba commented 6 years ago

https://github.com/dgladkov/react-native-uncontrolled-date-picker-ios doesn't work for me.

I believe this is not a RN issue because it doesn't work in Apple Calendar either.

img_1012 img_1013

kevinEsherick commented 6 years ago

For anyone having issues I recommend @dgladkov 's component. Works well for me https://github.com/dgladkov/react-native-uncontrolled-date-picker-ios

coderdave commented 6 years ago

Did this commit (https://github.com/facebook/react-native/commit/446ce49) in 0.55 fix this issue?

I was getting intermittent results in the first place, so it's hard to tell. 🤔

The comment for the new initialDate prop says: "The controlled state has known bugs which causes it to go out of sync with native. The initialDate prop is intended to allow you to have native be source of truth."

I'm using the initialDate prop now and I haven't seen the issue yet, but I still need more testing to be sure.

sam1463 commented 5 years ago

Although https://github.com/dgladkov/react-native-uncontrolled-date-picker-ios seems to fix this issue, it doesn't support the locale prop, which is needed for a work-around to support 24 hour time, so it's not really a solution for this issue.

grabbou commented 5 years ago

Closing this issue as various comments earlier in this thread suggest this is a bug in iOS that can be also reproduced in the Apple Calendar app.

There were also some other interesting workarounds suggested and alternative components.

DatePickerIOS is going to be extracted out soon to a separate repository where we will be able to commit more code, including workaround for this issue to be available out of the box.

lunatupini commented 5 years ago

This issue has been fixed on recently updates? I'm currently on version 0.59.9 and this appear to be fixed.

SpectorHacked commented 5 years ago

Still having this issue, using range, when logging the dates it logging the right date, but when formatting the date something is going wrong, react native 0.61:(

stefanocapitanio commented 4 years ago

Me too, same issue using countdown with React Native 0.59.9