Reputation: 583
I'm doing my first project with React and I've hit a wall in how to use the datetime-local field using the usual React logic.
For any given input field I would do as instructed in the React documentation.
The issue for me is that the datetime-local field has some annoying return-values. It returns an empty string in two cases. One case is when you use the built-in clear button and the other case is when it's set to an invalid date - For example 29th February 2015.
Since this is the case I can't just set the value of the field equal to the value of the event.target.value, since that would reset the field every time someone hits an invalid date. If I tell it to do nothing when encountering an empty return value that means that you can't use the clear button on the field anymore.
I've been unable to find anything related to this issue so I'm hoping that someone in here has an idea to solve it.
Upvotes: 17
Views: 26119
Reputation: 3092
The trick to solving this is to use the .validity.valid property of the input field (see https://developer.mozilla.org/en-US/docs/Web/API/ValidityState). If this is false, don't update the React state. This could look like this:
const [ datetime, setDatetime ] = useState('');
<input type="datetime-local"
value={(datetime || '').toString().substring(0, 16)}
onChange={handleChange} />
function handleChange(ev) {
if (!ev.target['validity'].valid) return;
const dt= ev.target['value'] + ':00Z';
setDatetime(dt);
}
Upvotes: 4
Reputation: 7324
Edit to fix getDay to getDate as getDay returns the day of the week, and getDate returns day of the month.
<input
type="datetime-local"
value={this.state.datetime}
onChange={e => this.handleChange('datetime', e)}
/>
Since its a controlled component you have to set a state value to read from. I set the current datetime in state like so...
state = {
datetime: `${new Date().getFullYear()}-${`${new Date().getMonth() +
1}`.padStart(2, 0)}-${`${new Date().getDate() + 1}`.padStart(
2,
0
)}T${`${new Date().getHours()}`.padStart(
2,
0
)}:${`${new Date().getMinutes()}`.padStart(2, 0)}`
};
and my handleChange is reuable for other text inputs like so :
handleChange = (field, e) => {
this.setState({ [field]: e.target.value });
};
Upvotes: 1
Reputation: 254
Edit to my edit, do not add 1 to day of month
Edit to josh's answer to fix getDay to getDate as getDay returns the day of the week, and getDate returns day of the month.
<input
type="datetime-local"
value={this.state.datetime}
onChange={e => this.handleChange('datetime', e)}
/>
Since its a controlled component you have to set a state value to read from. I set the current datetime in state like so...
state = {
datetime: `${new Date().getFullYear()}-${`${new Date().getMonth() +
1}`.padStart(2, 0)}-${`${new Date().getDate()}`.padStart(
2,
0
)}T${`${new Date().getHours()}`.padStart(
2,
0
)}:${`${new Date().getMinutes()}`.padStart(2, 0)}`
};
and my handleChange is reusable for other text inputs like so :
handleChange = (field, e) => {
this.setState({ [field]: e.target.value });
};
Upvotes: 0
Reputation: 583
I've solved this for now by setting the value using the JSX "defaultValue" attribute instead of "value".
This results in the field not being locked by the state variable which in turn makes it possible for me to make an onChange function that always updates the state but doesn't have any effect on the field itself.
By doing so the field behaves as expected and I can just submit whatever value is currently in my state.
The downside to solving it like this is that I'm not able to validate the date. Which means if someone tries to submit an invalid date it will just be stored as null in the database.
If anyone comes up with a more elegant solution I'd be glad to hear it.
Upvotes: 16