I am working on a form that has, among other fields, calendar entries - dates and times. The code is below:
{dates.map((d, i) => (
<div className="date-time-entry" key={i}>
<div className="date-picker">
<DatePicker
selected={new Date(d.startDate)}
dateFormat="dd MMM yyyy"
onChange={date => {
const newDates = [...dates]
newDates[i].startDate = date
setDates(newDates)
}}
/>
to
<DatePicker
selected={new Date(d.endDate)}
dateFormat="dd MMM yyyy"
onChange={date => {
const newDates = [...dates]
newDates[i].endDate = date
setDates(newDates)
}}
/>
{i ? (
<span
onClick={() => {
const newDates = dates.filter((d, k) => k !== i)
setDates(newDates)
}}
>
<FontAwesomeIcon icon={faTimes} />
</span>
) : null}
</div>
{dates[i].times.map((t, j) => {
return (
<div className="time-picker" key={j}>
at
<input
ref={(input) => { nameInput = input }}
value={t.start}
onChange={e => {
const newDates = [...dates]
newDates[i].times[j].start = e.target.value
setDates(newDates)
}}
/>{' '}
to{' '}
<input
ref={(input) => { nameInput = input }}
value={t.end}
onChange={e => {
const newDates = [...dates]
newDates[i].times[j].end = e.target.value
setDates(newDates)
}}
/>
<span
onClick={() => {
const newTimes = dates[i].times.filter((t, k) => k !== j)
const newDates = [...dates]
newDates[i].times = newTimes
setDates(newDates)
}}
>
<FontAwesomeIcon icon={faTimes} />
</span>
</div>
)
})}
<div
className="add-time"
onClick={() => {
const newDates = [...dates]
newDates[i].times = [
...newDates[i].times,
{ start: '12:00', end: '13:00' }
]
setDates(newDates)
}}
>
Add Time
</div>
</div>
)
)}
Then there is the useState()
hook defined as:
const [dates, setDates] = useState([
{
startDate: new Date(),
endDate: new Date(),
times: [{ start: '12:00', end: '13:00' }]
}
])
The problem that I am having is that whenever onChange is called - each keystroke the rerender occurs and my component loses focus.
I tried using onBlur
and defaultValue
instead of onChange
, but there is also a focus problem - two clicks are needed for the focus to switch because the first one is consumed by rerendering.
I also tried adding timeout to deal with onBlur
"losing" one click as below:
<input
defaultValue={t.start}
onBlur={e => {
const value = e.target.value
timeout = setTimeout(() => {
const newDates = [...dates]
newDates[i].times[j].start = value
setDates(newDates)
}, 0)
}}
onFocus={() => {
clearTimeout(timeout)
}}
/>
The last version does not lose focus anymore, yet instead, it loses state updates. Any suggestions as to how better handle this, or maybe even fix my existing code?