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

Dialog opens twice on Android #54

Closed
Niore opened this issue Nov 7, 2019 · 45 comments
Closed

Dialog opens twice on Android #54

Niore opened this issue Nov 7, 2019 · 45 comments

Comments

@Niore
Copy link

Niore commented Nov 7, 2019

If i open the Datepicker it opens twice on Android. I select in the first Dialog a date. then the same dialog opens again and i have to select a date again . After the second time it will accept the input. Can someone help me?

Thats the code of the component. Most of the components are just for styling:

const DatePickerInput = ({
  inputName,
  locale,
  labelKey,
  max,
  min,
}) => {
  const { values, setFieldValue } = useFormikContext();
  const [t] = useTranslation('validatedTextInput');
  const [showDatePicker, setShowDatePicker] = useState(false);
  const initialDate = values[inputName] || new Date();
  const [selectedDate, setSelectedDate] = useState(moment(initialDate).toDate());
  const datePlaceholderKey = 'datePlaceholder';
  return (
    <DatePickerContainer>
      <DatePickerLabel>
        {t(labelKey)}
      </DatePickerLabel>
      <DatePickerButtonContainer>
        <DatePickerButton
          onPress={() => setShowDatePicker(!showDatePicker)}
        >
          <DatePickerButtonText>
            {selectedDate
              ? moment(selectedDate).format('L')
              : t(datePlaceholderKey)}
          </DatePickerButtonText>
          <DatePickerButtonImage source={Calendar} />
        </DatePickerButton>
      </DatePickerButtonContainer>
      {
        showDatePicker && (
          <DateTimePicker
            mode="date"
            display="spinner"
            value={selectedDate}
            onChange={(event, value) => {
              setFieldValue(inputName, value);
              setShowDatePicker(!showDatePicker);
              // setSelectedDate(value);
            }}
            maximumDate={max}
            minimumDate={min}
            locale={locale}
          />
        )
      }
    </DatePickerContainer>
  );
};

Thx for your help

@tranhoangduong1994
Copy link

Did you resolve this problem @Niore?
I'm having a similar issue :/

@Niore
Copy link
Author

Niore commented Nov 11, 2019

no still got no solution for it.

@crcass
Copy link

crcass commented Nov 11, 2019

Have you tried setShowDatePicker(Platform.OS === 'ios' ? true : false);?
I remember having the same issue and I think the above was part of the solution.

@Freerider689
Copy link

Freerider689 commented Nov 12, 2019

The problem is because of the rerender queue when using useState hooks explained here
https://stackoverflow.com/a/54120412

To implicitly order the rerenders and avoid a second datepicker just do

onChange={(event, value) => {
 setShowDatePicker(Platform.OS === 'ios'); // first state update hides datetimepicker
 setFieldValue(inputName, value);
 setSelectedDate(value);
}}

To be more explicit I suggest the useStateWithCallback hook created by @rwieruch here
https://github.com/the-road-to-learn-react/use-state-with-callback/blob/master/src/index.js

And replace your current selectedDate state hook with the following:

const [selectedDate, setSelectedDate] = useStateWithCallback(
    initialDate,
    () => setShowDatePicker(Platform.OS === 'ios'),
); 

With this you no longer need to set the showDatePicker state onChange() so just this:

onChange={(event, value) => {
 setFieldValue(inputName, value);
 setSelectedDate(value);
}}

@vovka-s
Copy link

vovka-s commented Nov 14, 2019

I fixed this problem by closing the picker before handling it's value.
Swap setFieldValue and setShowDatePicker into onChange handler

onChange={(event, value) => {
    setShowDatePicker(!showDatePicker);
    setFieldValue(inputName, value);
   // setSelectedDate(value);
}}

Hope this will work

@vancepope
Copy link

@vovka-s I've been scratching my head all day on this one, thank you!

@mefjuu
Copy link

mefjuu commented Dec 20, 2019

Another solution is just creating a memoized wrapper for avoid render phase of native component:
const MemoizedDateTimePicker = React.memo((props) => <DateTimePicker {...props} />)

@androdri1998
Copy link

androdri1998 commented Feb 11, 2020

I fixed it with the following code:

const [state, setState] = useState({
  date: new Date(),
  mode: 'date',
  show: false
});
const onChange = (event, selectedDate) => {
  const currentDate = selectedDate || state.date;

  setState({...state, date: currentDate, show: false});
};
const showPicker = currentMode => {
  setState({...state, show: true});
};

{ state.show && 
  (<DateTimePicker
        testID="dateTimePicker"
        timeZoneOffsetInMinutes={0}
        value={state.date}
        mode={state.mode}
        is24Hour={true}
        display="default"
        onChange={onChange}
      />)
}

@nisiybr
Copy link

nisiybr commented Feb 19, 2020

The problem is because of the rerender queue when using useState hooks explained here
https://stackoverflow.com/a/54120412

To implicitly order the rerenders and avoid a second datepicker just do

onChange={(event, value) => {
 setShowDatePicker(Platform.OS === 'ios'); // first state update hides datetimepicker
 setFieldValue(inputName, value);
 setSelectedDate(value);
}}

To be more explicit I suggest the useStateWithCallback hook created by @rwieruch here
https://github.com/the-road-to-learn-react/use-state-with-callback/blob/master/src/index.js

And replace your current selectedDate state hook with the following:

const [selectedDate, setSelectedDate] = useStateWithCallback(
    initialDate,
    () => setShowDatePicker(Platform.OS === 'ios'),
); 

With this you no longer need to set the showDatePicker state onChange() so just this:

onChange={(event, value) => {
 setFieldValue(inputName, value);
 setSelectedDate(value);
}}

Works like a charm.
Thanks!

@james-greenwood
Copy link

For anyone who the above solution didn't work for, I was having an identical issue. Spent 3 days trying to figure it out and eventually realised it was caused by the debugger. Stopping debug fixed the issue for me.

@vovka-s
Copy link

vovka-s commented Apr 24, 2020

try that

const handleConfirm = (datetime) => {
   hideDatePicker() //must be first
   console.log("A date has been picked: ", datetime)  
   setChosenDate(moment(datetime).format('dddd Do MMMM YYYY à HH:mm'))
}

If not, try to use onChange callback instead of onConfirm
It works for me

@sackda75
Copy link

const handleConfirm = (datetime) => {
hideDatePicker() //must be first
console.log("A date has been picked: ", datetime)
setChosenDate(moment(datetime).format('dddd Do MMMM YYYY à HH:mm'))
}

Thank you very much ! It works !

@vishwa1937
Copy link

try that

const handleConfirm = (datetime) => {
   hideDatePicker() //must be first
   console.log("A date has been picked: ", datetime)  
   setChosenDate(moment(datetime).format('dddd Do MMMM YYYY à HH:mm'))
}

If not, try to use onChange callback instead of onConfirm
It works for me

@ vovka-s Thank you very much . It worked for me, you saved my day.

@sjonchhe
Copy link

For anyone who the above solution didn't work for, I was having an identical issue. Spent 3 days trying to figure it out and eventually realised it was caused by the debugger. Stopping debug fixed the issue for me.

This kind of solved it for me

@Luckygirlllll
Copy link

For anyone who the above solution didn't work for, I was having an identical issue. Spent 3 days trying to figure it out and eventually realised it was caused by the debugger. Stopping debug fixed the issue for me.

wow, that helped me as well, I spent about 4 hours trying to figure out what's wrong with the picker!

@MorenoMdz
Copy link

MorenoMdz commented Oct 6, 2020

The problem is because of the rerender queue when using useState hooks explained here
https://stackoverflow.com/a/54120412

To implicitly order the rerenders and avoid a second datepicker just do

onChange={(event, value) => {
 setShowDatePicker(Platform.OS === 'ios'); // first state update hides datetimepicker
 setFieldValue(inputName, value);
 setSelectedDate(value);
}}

To be more explicit I suggest the useStateWithCallback hook created by @rwieruch here
https://github.com/the-road-to-learn-react/use-state-with-callback/blob/master/src/index.js

And replace your current selectedDate state hook with the following:

const [selectedDate, setSelectedDate] = useStateWithCallback(
    initialDate,
    () => setShowDatePicker(Platform.OS === 'ios'),
); 

With this you no longer need to set the showDatePicker state onChange() so just this:

onChange={(event, value) => {
 setFieldValue(inputName, value);
 setSelectedDate(value);
}}

Thank you that solved for me.
I have it set this way if anybody needs a Modal component that supports both OS.
DatePickerModal

@rakefetWorkiz
Copy link

I tried all the above, and none helped me.
But using

onHide={() => setShowDatePicker(false)}

worked like a charm :)

@jkdc
Copy link

jkdc commented Oct 29, 2020

Hi!
for me work this example when you have two DateTimePicker in same page.

    const showTimepicker = (event: any) => {
        event.preventDefault()
        setShow(true);
    };
    <TextLinkButton onPress={(event) => showTimepicker(event)} label={label} />

I hope it works for you. A greeting!

@rossenv
Copy link

rossenv commented Nov 3, 2020

I tried all the above, and none helped me.
But using

onHide={() => setShowDatePicker(false)}

worked like a charm :)

react-native-datetimepicker has no onHide method

@ChanakaChampSoft
Copy link

I fixed this problem by closing the picker before handling it's value.
Swap setFieldValue and setShowDatePicker into onChange handler

onChange={(event, value) => {
    setShowDatePicker(!showDatePicker);
    setFieldValue(inputName, value);
   // setSelectedDate(value);
}}

Hope this will work

working !! Tada !!

@parth-koshta
Copy link

I fixed this problem by closing the picker before handling it's value.
Swap setFieldValue and setShowDatePicker into onChange handler

onChange={(event, value) => {
    setShowDatePicker(!showDatePicker);
    setFieldValue(inputName, value);
   // setSelectedDate(value);
}}

Hope this will work

Thanks @vovka-s

johnschoeman pushed a commit to Path-Check/gaen-mobile that referenced this issue Dec 9, 2020
Why:
we would like for the user details form to provide a good UX on both iOS and Android.

This commit:
Updates the styling on the form, especially to the DatePicker element.

Note that ReactNativeDatetimePicker has a UI bug on android that required an odd workaround with `setShowDatePickerAndroid(false)`
reference:
react-native-datetimepicker/datetimepicker#263
react-native-datetimepicker/datetimepicker#54
tokumori pushed a commit to AlohaSafe/gaen-mobile that referenced this issue Dec 31, 2020
…#744)

Why:
we would like for the user details form to provide a good UX on both iOS and Android.

This commit:
Updates the styling on the form, especially to the DatePicker element.

Note that ReactNativeDatetimePicker has a UI bug on android that required an odd workaround with `setShowDatePickerAndroid(false)`
reference:
react-native-datetimepicker/datetimepicker#263
react-native-datetimepicker/datetimepicker#54
@hiepnh610
Copy link

I fixed this problem by closing the picker before handling it's value.
Swap setFieldValue and setShowDatePicker into onChange handler

onChange={(event, value) => {
    setShowDatePicker(!showDatePicker);
    setFieldValue(inputName, value);
   // setSelectedDate(value);
}}

Hope this will work

Thank you so much!!!
You saved my life.

@daheeahn
Copy link

Another solution is just creating a memoized wrapper for avoid render phase of native component:
const MemoizedDateTimePicker = React.memo((props) => <DateTimePicker {...props} />)

Thanks! It works.

@kapilwhaval
Copy link

I tried this workaround. Works fine.

const DTPicker = ({ mode, onChange, value, placeholder, label,}) => {

    const [show, setShow] = useState(false);
    
    const handleDate = (e) => {
        if (e.nativeEvent.timestamp) onChange(e.nativeEvent.timestamp)
        setShow(Platform.OS === 'ios')
    }
    
    return <View>
        <TextInput placeHolder={placeholder} label={label} onFocus={() => setShow(true)} value={value} />
        {React.useMemo(() => {
            return show && <DateTimePicker
                value={new Date()}
                mode={mode}
                display="default"
                onChange={handleDate}
            />
        }, [show])}
    </View>
};

@mefjuu
Copy link

mefjuu commented Jan 22, 2021

I tried this workaround. Works fine.

Very weird use case for useMemo... Better try to extract it to React.memo'ized component

@kapilwhaval
Copy link

I tried this workaround. Works fine.

Very weird use case for useMemo... Better try to extract it to React.memo'ized component

Yes. We can refactor the component as you are saying. I have just added it here in single function, so that everyone can understand

@yayidg22
Copy link

yayidg22 commented Jan 30, 2021

I fixed this problem
onConfirm={selecteddate => {
setDate(selecteddate);
setPickerVisible(false);
} }

to

onConfirm={(selectedDate) => {
setPickerVisible(false);
setDate(selecteddate);
} }

@danibrear
Copy link

danibrear commented Feb 2, 2021

I had this issue with the dialog showing up again after I'd selected the date value. My solution that worked is:

const [show, setShow] = React.useState<boolean>(false);
const [birthday, setBirthday] = React.useState<string | undefined>(undefined);
const onChangeDate = useCallback((event, selectedDate) => {
    setBirthday(selectedDate);
    setShow(false);
  }, []);

// in the render
{show && (
  <View>
    <DateTimePicker
      testID="dateTimePicker"
      maximumDate={
        new Date(
          moment().subtract(13, "years").format("yyyy-MM-DD"),
        )
      }
      value={
        birthday
          ? new Date(birthday)
          : new Date(
              moment().subtract(13, "years").format("yyyy-MM-DD"),
            )
      }
      mode="date"
      display="default"
      onChange={(e, v) => {
        setShow(Platform.OS === "ios");
        onChangeDate(e, v);
      }}
    />
  </View>
  )}

hope this helps!

@laryssonalves
Copy link

I tried this workaround. Works fine.

const DTPicker = ({ mode, onChange, value, placeholder, label,}) => {

    const [show, setShow] = useState(false);
    
    const handleDate = (e) => {
        if (e.nativeEvent.timestamp) onChange(e.nativeEvent.timestamp)
        setShow(Platform.OS === 'ios')
    }
    
    return <View>
        <TextInput placeHolder={placeholder} label={label} onFocus={() => setShow(true)} value={value} />
        {React.useMemo(() => {
            return show && <DateTimePicker
                value={new Date()}
                mode={mode}
                display="default"
                onChange={handleDate}
            />
        }, [show])}
    </View>
};

This solution was the only one that works for me. Someone can explain to me how useMemo works on this case?

@Vigneshacme
Copy link

I fixed this problem by closing the picker before handling it's value.
Swap setFieldValue and setShowDatePicker into onChange handler

onChange={(event, value) => {
    setShowDatePicker(!showDatePicker);
    setFieldValue(inputName, value);
   // setSelectedDate(value);
}}

Hope this will work

thank you! it worked

@renatomh
Copy link

renatomh commented Sep 2, 2021

The suggested solution of closing the picker before anything else does not work for me. Also, I can't change the selected date.

What I believe it's happening in my case, is that due to the fact that two pickers are open, whenever I try selecting the new date on the visible picker, the picker behind it forces the date to be the previous selected value.

Here's what happens on my code when:

  • Closing the picker before setting the value:
    • Android emulator, when I close the picker, the second picker just pops and disappear, date does not change;
    • Android Physical Device: when I close the picker, the second picker also gets closed and it's not possible to change the date;
  • Setting the value before closing the picker:
    • On the Android emulator, when I close the picker, the second picker just pops and disappear, date does not change;
    • Android Physical Device, when I close the picker, the second picker becomes visible and it's possible to select the date;

Also, what's the default behaivour of 'onChange'? Should it get triggered whenever you click on a new date? Or only after you click 'Ok' to confirm the selected date?
If the first option is the default behaviour, I believe that this method is only getting attached to the 'second' picker, which is not visible at first.

Here's the relevant part of the current code I'm working with:

  const [usedAt, setUsedAt] = useState(new Date());
  const [showDatePicker, setShowDatePicker] = useState(false);
  ...

  return (
      ...

      {showDatePicker && (
        <DateTimePicker
          testID="dateTimePicker"
          value={usedAt}
          mode="date"
          is24Hour
          display="default"
          onChange={(e: Event, date?: Date) => {
            setShowDatePicker(Platform.OS === 'ios');
            setUsedAt(date || usedAt);
          }}
        />
      )}
      ...
  )

@renatomh
Copy link

renatomh commented Sep 2, 2021

The suggested solution of closing the picker before anything else does not work for me. Also, I can't change the selected date.

What I believe it's happening in my case, is that due to the fact that two pickers are open, whenever I try selecting the new date on the visible picker, the picker behind it forces the date to be the previous selected value.

Here's what happens on my code when:

  • Closing the picker before setting the value:

    • Android emulator, when I close the picker, the second picker just pops and disappear, date does not change;
    • Android Physical Device: when I close the picker, the second picker also gets closed and it's not possible to change the date;
  • Setting the value before closing the picker:

    • On the Android emulator, when I close the picker, the second picker just pops and disappear, date does not change;
    • Android Physical Device, when I close the picker, the second picker becomes visible and it's possible to select the date;

Also, what's the default behaivour of 'onChange'? Should it get triggered whenever you click on a new date? Or only after you click 'Ok' to confirm the selected date?
If the first option is the default behaviour, I believe that this method is only getting attached to the 'second' picker, which is not visible at first.

Here's the relevant part of the current code I'm working with:

  const [usedAt, setUsedAt] = useState(new Date());
  const [showDatePicker, setShowDatePicker] = useState(false);
  ...

  return (
      ...

      {showDatePicker && (
        <DateTimePicker
          testID="dateTimePicker"
          value={usedAt}
          mode="date"
          is24Hour
          display="default"
          onChange={(e: Event, date?: Date) => {
            setShowDatePicker(Platform.OS === 'ios');
            setUsedAt(date || usedAt);
          }}
        />
      )}
      ...
  )

I've managed to get it to work now. Basically, I had to create a function with the 'useCallback' hook to deal with the date selection event like so:

  const handleDateChange = useCallback(
    (event: Event, date: Date | undefined) => {
      if (Platform.OS === 'android') {
        setShowDatePicker(false);
      }
      if (date) setUsedAt(date);
    },
    [],
  );

  return (
      ...

      {showDatePicker && (
        <DateTimePicker
          testID="dateTimePicker"
          value={usedAt}
          mode="date"
          is24Hour
          display="default"
          onChange={handleDateChange}
        />
      )}
      ...
  )

@carlossison75
Copy link

I'm 2 years late and I don't know if this is fixed already since I didn't read everything, but whoever still having this issue, just close the modal first before setting the new date.

@vonovak
Copy link
Member

vonovak commented Mar 11, 2022

Hello 👋 ,
this issues should be resolved in #574

With that, I'm closing the issue,
Thank you! 🙂

@vonovak vonovak closed this as completed Mar 11, 2022
@Shail-Patel-1
Copy link

try that

const handleConfirm = (datetime) => {
   hideDatePicker() //must be first
   console.log("A date has been picked: ", datetime)  
   setChosenDate(moment(datetime).format('dddd Do MMMM YYYY à HH:mm'))
}

If not, try to use onChange callback instead of onConfirm It works for me

Thank you so much
this work like charm
i was struggling from many days

@chefk5
Copy link

chefk5 commented Sep 8, 2022

try that

const handleConfirm = (datetime) => {
   hideDatePicker() //must be first
   console.log("A date has been picked: ", datetime)  
   setChosenDate(moment(datetime).format('dddd Do MMMM YYYY à HH:mm'))
}

If not, try to use onChange callback instead of onConfirm It works for me

Still works!

@Suliman-A
Copy link

DatePickerModal

which debugger do you mean exactly?

@iamemiliano
Copy link

I hope you are doing well. I have a problem with react native on Android. In IOS, it works well but when i open a componant who includes a DataTimePicker in Android, it automatically shows me the DatatimePicker. Do you know what the problem is in my code ?

             <View style={styles.dateContainer}>
                <Text style={styles.customLabelDate}>
                    Date de naissance
                </Text>
                <DateTimePicker
                    testID="dateTimePicker"
                    value={stateForm.dateOfBirth}
                    mode="date"
                    is24Hour={true}
                    display="calendar"
                    onChange={(event, value) => {
                        dispatchStateForm({
                            type: UPDATE,
                            payload: { name: "dateOfBirth", value: value },
                        });
                    }}
                    style={styles.dateTime}
                    locale="fr-FR"
                    textColor="red"
                />
            </View>

This is the result :

image

Thanks in advance.

@chefk5
Copy link

chefk5 commented Oct 15, 2022

@iamemiliano you need to wrap it with a state and show it according to it. Have a look in the example

            {show && (
            <DateTimePicker
              testID="dateTimePicker"
              timeZoneOffsetInMinutes={tzOffsetInMinutes}
              minuteInterval={interval}
              maximumDate={maximumDate}
              minimumDate={minimumDate}
              value={date}
              mode={mode}
              is24Hour
              display={display}
              onChange={onChange}
              textColor={textColor || undefined}
              accentColor={accentColor || undefined}
              neutralButtonLabel={neutralButtonLabel}
              disabled={disabled}
            />
          )}`

@DanielDelneri
Copy link

I have a problem where the dialog opens twice and I can't pick a date (after I click on a date it get back to the starting one instantly).

`const handleData1 = (event,date)=>{
setShowstart(Platform.OS === 'ios')

if(event.type === "set"){
  setStart(date)
  setShowstart(false)
}

}`

<Text onPress={()=>setShowstart(true)}>{start.toDateString()}</Text> {showStart==true &&(<View> <DateTimePicker value={start} mode="date" textColor='white' style={{ marginBottom:30, marginTop:30, zIndex:1000,}} themeVariant='dark' onChange={handleData1} accentColor="white"/> </View>) }

I've tried to console log the event type and the value of showStart and for example on the dismiss I get 2 console log of event.type dismiss and after I click on the first dialog cancel the second one is still open and the value of showStart is false (how is it possible that even if the value showStart is false it still render the dialog)

@ShizaSiddiqui
Copy link

setShowCalendarToDate(Platform.OS === 'ios' ? true : false); //first line of onChange function

This will solve the android issue. But if you do not do this, iOS will start misbehaving. For example, if you set either month, year, or date, the iOS picker will disappear before letting you select all three.

@Onikiri1
Copy link

hi, im working on a task app, and when i use te DateTimePicker, it opens twice and doesnt show the selected date.
i've tried all above and nothing really worked,
this is what im working on
<DateTimePicker value={date} mode={"time"} is24Hour={true} onChange={(event, value) => { setShowDatePicker(!showDatePicker); setDate(selectedDate); }} style={{width: "80%"}} />

@Shail-Patel-1
Copy link

Shail-Patel-1 commented Dec 19, 2022

This is what you might have done

 const handleConfirm = selectedDateFromPicker => {
    Keyboard.dismiss();
    setDate(prevState => ({
      ...prevState,
      [type]: Helper.convertToIsoDateString(selectedDateFromPicker),
    }));
   **hideDatePicker();** --> _HERE_

You should do something like this

 const handleConfirm = selectedDateFromPicker => {
  **hideDatePicker();** --> _HERE_
    Keyboard.dismiss();
    setDate(prevState => ({
      ...prevState,
      [type]: Helper.convertToIsoDateString(selectedDateFromPicker),
    }));

You should first hide the date picker than you can do any operations you want
This will work. @Onikiri1

I had the same issue. fixed this way and working fine
if you don't get my code let me know i will show you the working of it

Here is the elaborated ansswer

Check this answer

@R-s-rawat
Copy link

yes same here in deadline stage

@SadatJubayer
Copy link

If anyone is still facing this issue, you can try this easy fix:

const [showDatePicker, setShowDatePicker] = useState(false);

{showDatePicker && (
  <DateTimePicker
    value={new Date()}
    mode={'date'}
    minimumDate={moment().startOf('day').toDate()}
    onChange={(_, newDate) => {
      setShowDatePicker(() => {
        setDate(newDate);
        return false; 
      });
    }}
  />
)}

In onChange, setShowDatePicker's callback function updates the date and then updates the showdatePicker state by returning false.

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

No branches or pull requests