Allowing Null Value for React-Native-DateTimePicker - reactjs

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?

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.

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:
text is used to display the current value or "No Date Selected".
When the user clicks the text, the date time picker is shown to let them select a date
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'
}
}
)

Related

React-datepicker with custom input with manual typing

I am trying to create react-datepicker component with custom styled input:
<DatePicker
selected={startDate}
customInput={<CustomInput inputRef={inputRef} />}
onChangeRaw={(e) => handleChangeRaw(e)}
onChange={(date: Date | null) => {
setStartDate(date);
}}
/>
const CustomInput = forwardRef((props: any, ref) => {
return <Input {...props} ref={ref} />;
});
I wanted to introduce validation (just simple validation that whatever user inputs will be formatted to the date so user cannot just type whatever he wants).
I created a function that would be formatting the input using onChangeRaw attribute in react-datepicker:
const handleChangeRaw = (date) => {
date.currentTarget.value = format(startDate, "dd/MM/yyyy");
};
However after using this I cannot type anymore. Anyone has idea how to use manual typing with custom input in react-datepicker with simple validation function?
Here is recreated issue in the sandbox:
https://codesandbox.io/s/react-datepicker-custominput-sample-forked-elx310?file=/src/App.tsx
selected={startDate}
Here react-datepicker expects a Date object.
However, when you trigger handleChangeRaw, you get an change event, with some value (date.currentTarget.value), but thats not in the Date notation that datepicker expects.
So my take on this would be;
onChangeRaw, convert the input value to a new Date.
If the date isn't valid, just ignore the change
Otherwise, create a new Date, and use setStartDate to change to that new date
const handleChangeRaw = (date) => {
const newRaw = new Date(date.currentTarget.value);
if (newRaw instanceof Date && !isNaN(newRaw)) {
setStartDate(newRaw);
}
};
Try it online
If I understand your issue correctly, you can add the following type to fix the typing.
const handleChangeRaw = (date: React.FocusEvent<HTMLInputElement>) => {
if (!startDate){
return
}
date.currentTarget.value = format(startDate, "dd/MM/yyyy");
};
date should have the type React.FocusEvent<HTMLInputElement>. Since startDate can be null, we have to check before using it. In this case I just return from the function. But you could also assign a default value here or something else.

Modal shows previous data

I have a little "bug" here. I have a modal that receives a text, that corresponds a message to show, a state and a property to restore the values of the states when the modal close. In the main, I have three states, each one represents different situations and each show a unique message. The problem occurs when I run the app, choose a date in a datetimepicker, evaluate that in a function, and then I sent that result in a conditional, if "result" is in a determined range -> I set a specific state, else, set another state. Once a state is true, the modal opens with the specific text.
The problem take place when the modal open, the information correspond to a previous "result". I tried to debug with console logs, and I saw that the states doesn't update.
Is it possible that the states take a time to update? Or could be a problem with the cache of the device?
I attach the code below.
Here are the definition of the states
const [modalVisible1, setModalVisible1] = useState(false)
const [modalVisible2, setModalVisible2] = useState(false)
const [modalVisible3, setModalVisible3] = useState(false)
This is the conditional that I explained. picker_value is a variable that take an option from a menu by the user.
const setModal = () => {
(picker_value ===2 && (result>6 && result<150)) ? setModalVisible1(true) : setModalVisible2(true)
if(picker_value == 3)
setModalVisible3(true)
}
And finally, I have this expression (one for each state), to show the modal with the respective message
{modalVisible1 && <MyModal
message={"Test123"}
state={modalVisible1}
restore={resetModal}
/>
}
And I detach the modal
export const MyModal = (props) => {
return (
<Modal visible={props.state}
transparent={true}
animationType='slide'
>
<View style={styles.container}>
<Text>{props.message}</Text>
<TouchableOpacity onPress={() => props.restore()}>
</TouchableOpacity>
</View>
</Modal>
);
EDIT: onChange Method. Date is the current day, and days is a state that saves the result from process_days (calculates the diff between 2 dates)
const [days, setDays] = useState(0)
const onChange = (event, selectedDate) => {
setShow(Platform.OS === 'ios')
setSelectedDay(selectedDate)
setDays(process_days(date, selectedDate))
setPickerValue(formData.pickerValue)
setModal()
}
Reset states
const resetModal = () => {
setModalVisible1(false)
//same for each modal
}

Proper way to change calendar date clear icon in DatePicker for AntD

I want to change the clear icon of a date picker from the ant design framework.
Is it possible ?
Reason - I want to reset the date to today when it clicks. So close-icon is not appropriate.
Since antd uses rc-picker to create DatePicker component, you can use clearIcon attribute to customize clear icon, but since you need to reset datePicker's value to today with click on clear, you need to do a bit more to handle this requirement, here is an example:
function PickerWithCustomClear() {
const [date, setDate] = React.useState(null);
const [shoudlReset, setShouldReset] = React.useState(false);
const onChange = (date) => {
setDate(date);
if (!date && shoudlReset) {
setDate(moment());
setShouldReset(false);
}
};
return (
<DatePicker
onChange={onChange}
allowClear={true}
value={date}
clearIcon={
// you can use every element here
<SyncOutlined onMouseDown={(e) => setShouldReset(true)} />
}
/>
);
}
I've implemented an example you can find it Here.

Is there a standard design pattern for viewing + editing a record in React / Material-UI?

Frequently I have a record of data to display to the user, and I want to display an Edit button allowing that data to be modified:
Create Pizza:
sauce: Margherita
cheese: Gorgonzola
radius: 12
[EDIT] [ORDER]
Is there a standard pattern for this in React / Material-UI? It seems silly to implement the same view twice, once with (readonly) <div>s or whatever and once with <input>s. Is there a better way? (Perhaps there's a Material-UI component with an "editable" prop, for instance.)
Take a look at api for text field. You'll see that you have a couple of ways of doing what you are looking.
You could set disabled={true}
You also get inputProps, as an example
<TextField id="time" type="time" inputProps={{ readOnly:true }} />
All the other elements should also have one or the other or both.
I usually have a wrapper component that accepts props such as ({isReadOnly: boolean}) & within that is the "native" React MUI component & then I send back editable or a read only component back to the caller.
EDIT:
interface MyTextFieldProps extends StandardTextFieldProps {
isEditable?: boolean;
// send in any other props necessary to render your non editable
// portion
}
const MyTextField: FC<MyTextFieldProps> = (props) => {
const { isEditable, ...other } = props;
return <>{props.isEditable ? <TextField {...other}></TextField> : <span>text:{props.value}</span>}</>;
};

How to access the text inside a TextInput without having the value prop defined?

I want to create a form without using useState to control the inputs because I only need the inputs values when the user presses the button, so I want to access/know the input value at that time only. I also think that updating the screen after each text input is not a good practice.
Let's assume that we have the following structure:
const Home = () => {
// const [inputValue, setInputValue] = useState(""); => I want to avoid that
const inputRef = useRef<TextInput>(null);
return (
<View>
<TextInput
style={styles.input}
ref={inputRef}
// value={inputValue} => I want to avoid that
// onChangeText={(text) => setInputValue(text)} => I want to avoid that
/>
</View>
);
};
We all know that when we have one TextInput on screen, we can type in it, without specifying the value prop and onChangeText, that we would normally use useState for that.
What I want to do is simply access the text that was input, and I would use a ref for that using useRef hook, but the problem is that using useRef I have access to TextInput props but the value is undefined because I am not specifying that. Am I missing something or there is no way to do that without using a useState for defining a value and onChangeText?
TextInput by nature is a controlled component.
However, to reach the behavior you need, you can use onChangeText to update the value wanted at the end:
<TextInput
ref={inputRef}
onChangeText={text => {
if(inputRef.current) {
inputRef.current.value = text;
}
}}
/>
I recommend applying best practices recommended for the component, or checking form libraries like react-hook-form.
Based on #Majed Badawi answer I could find the following solution.
const inputRef = useRef<TextInput>(null);
<TextInput
ref={inputRef}
onChangeText={(text) => (inputRef!.current!.context = text)}
/>
The !. operator will make sure that nothing is null, and the context of the TextInput is a readable property that you can set and read values.

Resources