Allowing Null Value for React-Native-DateTimePicker

4.1k Views Asked by At

I have a React Native application with a form for entering data. One of the fields is a DateTime value, but is optional. In other words, the user can choose a date if they want one, but can leave it blank/null if the field is not relevant.

However, when I assign null to the date prop of the DateTimePicker, I get an exception that null is not an object. How can allow the user to choose a date if they want to, but leave it "null" if they don't want a date in that field.

     <DateTimePicker
      isVisible={isDatePickerVisible}
      mode={mode}
      onConfirm={handleConfirm}
      onCancel={hideDatePicker}
      date={selectedDate}
      onBlur={onBlur}
    />

Is there a property I am missing? Is there a different component I can use to accomplish this?

3

There are 3 best solutions below

0
On BEST ANSWER

Thank you to @david-scholz for the insight into the react-native-datetimepicker library and the lack of null support. With that said, his answer doesn't provide the functionality requested.

I manage to implement it with a custom component which wraps the react-native-modal-datetimepicker library. The basic idea is to use the picker only when the user wants to pick a date.

To accomplish this:

  1. text is used to display the current value or "No Date Selected".
  2. When the user clicks the text, the date time picker is shown to let them select a date
  3. There is a button to click if the user wants to clear the date and go back to null

Note: This was intended to be used with Formik (in my use case) so the date / time value being passed back is in string format (it works better with Formik).

Here is the code, in case anyone can use it. It should be considered MIT License.

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

const isNullOrWhitespace = ( input ) => {
  return !input || !input.trim();
}

export default function NullableDatePicker (props) {
    const {value, onChange, onBlur} = props;
    const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
    const [selectedDate, setSelectedDate] = useState(isNullOrWhitespace(value) ?  new Date() : new Date(value));
    const [hasDate, setHasDate] = useState(isNullOrWhitespace(value) ? false : true);

    const showDatePicker = () => {
      setDatePickerVisibility(true);
    };
  
    const hideDatePicker = () => {
      setDatePickerVisibility(false);
    };
  
    const handleConfirm = date => {
      hideDatePicker();
      setHasDate(true);
      setSelectedDate(new Date(date));  
      onChange(valueToString(date));
    };
  
    const valueToString = selectedDate => {
        return moment(selectedDate).format('YYYY-MM-DD');
    }

    const clearDate = () => {
        setHasDate(false);
        onChange('');
    }
  
    return (
      <View>
        <View style={styles.fixToText}>
        <Text style={styles.dateText} onPress={showDatePicker}>{hasDate ? valueToString(selectedDate) : 'No Date Selected'}</Text>
        <Text>   </Text>
        <TouchableOpacity 
            title="Clear"
            onPress={() => clearDate()}
            disabled={!hasDate}
            style={styles.button}
        >
           <Text>Clear</Text> 
        </TouchableOpacity>
        </View>
        <DateTimePickerModal
          isVisible={isDatePickerVisible}
          mode='date'
          onConfirm={handleConfirm}
          onCancel={hideDatePicker}
          date={selectedDate}
          onBlur={onBlur}
        />
      </View>
    );  
  }
  const styles = StyleSheet.create(
      {
        fixToText: {
            flexDirection: 'row'
        },
        button: {
            alignItems: "center",
            backgroundColor: "lightblue",
            padding: 5,
            height: 30,
            width: 50
        },
        dateText:{
            height: 30,
            textAlignVertical: 'center'
        }
      }
  )
2
On

Notice that

Please note that this library currently exposes functionality from UIDatePicker on iOS and DatePickerDialog + TimePickerDialog on Android, and CalendarDatePicker +TimePicker on Windows.

These native classes offer only limited configuration, while there are dozens of possible options you as a developer may need. It follows that if your requirement is not supported by the backing native views, this library will not be able to implement your requirement. When you open an issue with a feature request, please document if (or how) the feature can be implemented using the aforementioned native views. If those views do not support what you need, such feature requests will be closed as not actionable.

as provided in the react-native-datetimepicker documentation.

The date prop that you are using is one of them. For iOS it exposes a field of UIDatePicker which is

The initial date that the date picker will display. Defaults to the current date, but you can set a custom value. This attribute is equivalent to setting the date property programmatically.

However, as stated in the react-native-datetimepickerdocumentation its usage is limited. The correct prop that we need to use is the value prop which

Defines the date or time value used in the component.

This field is required. We cannot unset it. There will always be some default value which needs to be explicitly set initially.

However, what you want to achieve can be done differently. You need to handle the form value yourself in a state of the form. Thus,

const [date, setDate] = useState()

The state date in your form is initially undefined. The value prop of your DateTimePicker is not.

The DateTimePicker gets an onConfirmMethod. If that is fired set your date state to the value prop passed to the onConfirmMethod as follows

const onConfirm = (selectedDate) => {
    setDate(selectedDate)
}

<DateTimePicker
      isVisible={isDatePickerVisible}
      mode={mode}
      onConfirm={onConfirm}
      onCancel={hideDatePicker}
      value={date}
      onBlur={onBlur}
/>
}

Usually you want the user to allow to delete the field afterwards. You need to implement this yourself, since value cannot be unset. If the selected date is visualized in some kind of InputField, then this can easily be done.

The data of your form is then decoupled from your DateTimePicker value prop. I'm afraid that there is no other way, at least with this library.

0
On

    <DateTimePicker
     testID="dateTimePicker"
     value={date || new Date()}
     is24Hour={true}
     onChange={ (event, selectedDate) =>{
     console.log(event.type); // if type is "dismissed" user press cancel
     }
    />

Here is the solution, onChage callback take argument "event" which is object with an attribute "type" so you can detect which button is pressed. On this way you can setDate only if event.type!=="dismissed" and close date dialog.