Форма реагирования теряет фокус на обновление - PullRequest
4 голосов
/ 08 марта 2020

Я работаю над формой, в которой, помимо прочего, есть записи календаря - даты и время. Код ниже:

  {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>
    )
  )}

Затем есть хук useState(), определенный как:

const [dates, setDates] = useState([
  {
    startDate: new Date(),
    endDate: new Date(),
    times: [{ start: '12:00', end: '13:00' }]
  }
])

Проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда вызывается onChange, - каждое нажатие клавиши происходит повторное рендеринг, и мой компонент теряет фокус.

Я пытался использовать onBlur и defaultValue вместо onChange, но есть и проблема фокусировки - для переключения фокуса необходимы два щелчка, потому что первый один расходуется при повторном рендере вместо этого он теряет обновления состояния. Любые предложения о том, как лучше справиться с этим, или, возможно, даже исправить мой существующий код?

1 Ответ

0 голосов
/ 09 марта 2020

Я думаю, что не очень хорошо иметь следующий код:

{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>
      )
    })}

Каждый раз, при любом изменении даты, все компоненты будут перерисовываться заново. Каждый раз в качестве отдельного компонента вы должны иметь средство выбора, например:

const changeHandler = (i, j, x, val) => {
  const newDates = [...dates]
  newDates[i].times[j][x] = val
  setDates(newDates)
}
const clickHandler = (i, j) => {
  const newTimes = dates[i].times.filter((t, k) => k !== j)
  const newDates = [...dates]
  newDates[i].times = newTimes
  setDates(newDates)
}
{dates[i].times.map((t, j) => {
  return (
    <MyTimePicker time={t} k={"time_" + j} changeHandler={( val,  x="start" ) => changeHandler(i, j, x, val)} clickHandler={() => clickHandler(i, j)}>
  )
 })}

и в другом файле «my-timepicker. js», вы должны определить свой компонент:

import React, { useRef } from "react";
const areEqual = (prevProps, nextProps) => {
  if(JSON.stringify(prevProps.time) == JSON.stringify(nextProps.time)) return true;
  return false;
}
const MyTimePicker = React.memo(({changeHandler, clickHandler, time }) => {
   const atInputRef = useRef();
   const toInputRef = useRef();
   return ( 
     <div className="time-picker">
       at
       <input
          ref={atRef}
          value={time.start}
          onChange={e => {
            changeHandler(e.target.value)
          }}
       />{' '}
       to{' '}
       <input
         ref={toRef}
         value={time.end}
         onChange={e => {
           changeHandler(e.target.value, "end")
         }}
       />
       <span
         onClick={clickHandler}
       >
         <FontAwesomeIcon icon={faTimes} />
       </span>
     </div>
   )
}, areEqual);
...