Reputation: 53526
I came across this weird behavior that I cannot explain. The sandbox for the code can be found here.
I'm trying to add the possibility to reset a readOnly
field by pressing DEL or BACKSPACE but it does not work. While trying to reproduce the issue, I can reset it when pressing a button, but not when typing on the keyboard; why?
import React, { useCallback, useState } from "react";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import AdapterDayjs from "@mui/lab/AdapterDayjs";
import LocalizationProvider from "@mui/lab/LocalizationProvider";
import DatePicker from "@mui/lab/DatePicker";
import Typography from "@mui/material/Typography";
import frLocaleDate from "dayjs/locale/fr";
export default function BasicDatePicker() {
const [value, setValue] = useState(null);
const [message, setMessage] = useState("");
const handleChanged = useCallback((newValue) => {
setMessage("Value changed to " + newValue);
setValue(newValue);
}, []);
const handleInputKeyDown = useCallback((event) => {
if (event.keyCode === 8 || event.keyCode === 46) {
setMessage("Reset through handleInputKeyDown");
setValue(null);
}
}, []);
const handleReset = useCallback(() => {
setMessage("Reset through handleReset");
setValue(null);
}, []);
return (
<LocalizationProvider dateAdapter={AdapterDayjs} locale={frLocaleDate}>
<Typography>Try pressing DEL or BACKSPACE to clear</Typography>
<DatePicker
clearable
showTodayButton
label="Basic example"
inputFormat="LL"
value={value}
onChange={handleChanged}
renderInput={({ InputProps, inputProps, ...props }) => (
<TextField
fullWidth
margin="dense"
size="small"
onKeyDown={handleInputKeyDown}
InputProps={{ ...InputProps }}
inputProps={{ ...inputProps, readOnly: true, placeholder: "" }}
{...props}
/>
)}
/>
<Button onClick={handleReset}>Reset</Button>
<Typography>
Debug: <pre>{message}</pre>
</Typography>
<Typography>
Current value: <pre>{JSON.stringify(value)}</pre>
</Typography>
</LocalizationProvider>
);
}
I tried to delay the setValue(null)
inside of the handleInputKeyDown
function, but even if the value is null
, the input still displays a value.
The problem occurs only when the field has focus. If I change the handleInputKeyDown
like this, press DEL and click away from the input field (i.e. causing it to lose focus), then the field is reset after 2 seconds (when the setTimeout
calls the handler function).
const handleInputKeyDown = useCallback((event) => {
if (event.keyCode === 8 || event.keyCode === 46) {
setTimeout(() => {
setMessage("Reset through handleInputKeyDown");
setValue(null);
}, 2000);
}
}, []);
I tried adding a ref to the DatePicker
component, then calling ref.current.blur();
, but it does not work, and the element does not lose focus.
Following Emanuele Scarabattoli's answer and explanation, this also seems to work in my particular case :
const handleInputKeyDown = useCallback((event) => {
//event.preventDefault();
if (event.keyCode === 8 || event.keyCode === 46) {
setTimeout(() => {
event.target.blur(); // blur outside the scope of keyDown
setMessage("Reset through handleInputKeyDown");
setValue(null);
}, 0);
}
}, []);
Which raises this other issue : When the field is focused, changing the value will not update the display. This is problematic when the user selects the field, but the program updates it programmatically; the values gets updated but the user does not have a feedback of the changes.
Upvotes: 2
Views: 2953
Reputation: 4469
After your suggestion in the update, I got this workaround:
const handleInputKeyDown = useCallback((event) => {
event.target.blur() // As you suggested
event.preventDefault() // This does the trick
if (event.keyCode === 8 || event.keyCode === 46) {
setMessage("Reset through handleInputKeyDown");
setValue(null);
}
}, [setMessage, setValue]); // Better to add, as per React documentation
Possible explanation
Disclaimer: this explanation is not demonstrated and it is not necessarily true!
Since the DEL
key, as default behavior, is used as a key to delete a character, my impression is that the default behavior have to be prevented to make it work differently. Probably this is due to something related to event.target.value
in the event
. I mean, possibly, the browser, in case of DEL
or ESC
pressed, is propagating an event that, at the end, does something like valueOfTheInput = event.target.value
, even if the value
itself is not changed, so we need to avoid this.
Upvotes: 3