Datetimepicker: Dialog opens twice on Android

Created on 7 Nov 2019  路  30Comments  路  Source: react-native-datetimepicker/datetimepicker

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

Most helpful comment

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

All 30 comments

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

no still got no solution for it.

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.

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

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

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

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

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 &&
( testID="dateTimePicker"
timeZoneOffsetInMinutes={0}
value={state.date}
mode={state.mode}
is24Hour={true}
display="default"
onChange={onChange}
/>)
}
```

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!

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.

I'm having the same issue with DateTimePickerModal. I've read all the comments but it can't fix it. Can anyone help me ? Thanks

import React, { useState } from "react"
import { View, StyleSheet, Text, TouchableOpacity } from "react-native"
import DateTimePickerModal from "react-native-modal-datetime-picker"
import moment from 'moment'
import 'moment/locale/fr'

const App = () => {

const [isDatePickerVisible, setDatePickerVisibility] = useState(false)

const [chosenDate, setChosenDate] = useState('')

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

const showDatePicker = () => {
setDatePickerVisibility(true)
}

const hideDatePicker = () => {
setDatePickerVisibility(false)
}

return (

<View style={styles.container}>

  <Text style={styles.displayDate}>{chosenDate}</Text>

  <TouchableOpacity style={styles.buttonStyle} onPress={showDatePicker}>
      <Text style={styles.buttonFont}>Select a date and time</Text>
  </TouchableOpacity>

  <DateTimePickerModal
    isVisible={isDatePickerVisible}
    mode="datetime"
    onConfirm={handleConfirm}
    onCancel={hideDatePicker}
    display="default"

  />

</View>

)
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffff',
alignItems: 'center',
justifyContent: 'center'
},
displayDate: {
marginVertical: 20,
fontSize: 20,
fontWeight: 'bold',
color: '#8e44ad'
},
buttonStyle: {
paddingTop: 15,
paddingBottom: 15,
marginLeft: 30,
marginRight: 30,
backgroundColor: '#FDA7DF',
borderRadius: 40,
borderWidth: 2,
borderColor: '#1B1464',
width: 250
},
buttonFont: {
textAlign: 'center',
color: '#ffff',
fontWeight: 'bold',
fontSize: 20
}
})

export default App

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

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 !

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.

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

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!

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

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

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

worked like a charm :)

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!

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

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 !!

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

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.

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.

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

I tried this workaround. Works fine.

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

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

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

to

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

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!

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?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Kamalnrf picture Kamalnrf  路  3Comments

otaviogaiao picture otaviogaiao  路  5Comments

ArturDabrowski picture ArturDabrowski  路  3Comments

FDiskas picture FDiskas  路  3Comments

sorinsi picture sorinsi  路  5Comments