【问题标题】:React form loses focus on updateReact 表单失去了对更新的关注
【发布时间】:2020-06-20 14:28:50
【问题描述】:

我正在处理一个表单,其中包含日历条目(日期和时间)等字段。代码如下:

  {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 - 每次击键都会发生重新渲染并且我的组件失去焦点。

我尝试使用onBlurdefaultValue 代替onChange,但也存在焦点问题 - 需要单击两次才能切换焦点,因为第一次被重新渲染消耗。

我也尝试过添加超时来处理onBlur“输”的一键点击如下:

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

最后一个版本不再失去焦点,而是失去状态更新。关于如何更好地处理这个问题,或者甚至修复我现有的代码的任何建议?

【问题讨论】:

  • 这只是一个提示,但我很确定问题出在更改键上。当密钥更改时,组件不会在同一个实例上更新,而是卸载并安装一个新实例。这也涉及到 DOM 对象,这将明确焦点。所以这可能不是答案,但它是一个常见的原因。

标签: reactjs dom-events react-state rerender react-effects


【解决方案1】:

我觉得下面的代码不是个好主意:

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

【讨论】:

  • 试过你的代码。认为您的 nameInput 可能未定义。看不出行为有什么不同。
  • @IgorShmukler 我已经编辑了修复 nameInput 的答案,并且我使用了 React.memo 来确保如果时间值没有改变,组件不会重新渲染
  • 现在,它的工作方式与原始版本完全一样 - 失去焦点。
  • @IgorShmukler 另外,我注意到代码中有一个错误。您的 keys 应该有一个前缀。我为时间数组添加了time 前缀,在代码顶部,您应该将日期更改为以下内容:{dates.map((d, i) =&gt; ( &lt;div className="date-time-entry" key={"date_" + i}&gt;
  • 我认为这不是问题所在。您是否尝试过运行自己的代码?谢谢。
猜你喜欢
  • 1970-01-01
  • 2019-12-16
  • 2014-10-12
  • 2020-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-06
相关资源
最近更新 更多