JavaScript 的 .setSelectionRange() 与 React Hooks 不兼容吗?

6 javascript html-input reactjs react-hooks

这个问题改进了 r/reactjs 上的一个问题

我有一个控件input,我可以通过编程方式更改其值。我想使用它.setSelectionRange()来保持输入中的插入符位置。

但这不起作用:默认情况下,每次重新渲染都会自动将选择范围设置为输入的末尾

此沙箱中说明了该问题,原始问题的作者以 10 毫秒的setTimeout()延迟修复了该问题。

如何在不使用与 Hooks 不兼容的setTimeout()or 的情况下实现此目的?getSnapshotBeforeUpdate()

Ric*_*sen 3

我看到的基本问题是.setSelectionRange()在模板中内联使用,并且应该包装在useEffect().

我还将拉出选择处理程序,只是为了更整洁(根据handleDomainChange()handleSubmit())。

useEffect 用于选择更新

const[selection, setSelection] = useState()

useEffect(() => {
  if (!selection) return;  // prevent running on start
  const {start, end} = selection;
  inputEl.current.focus();
  inputEl.current.setSelectionRange(start, end);
}, [selection])

const handleSelection = (e) => {
  const start = inputEl.current.selectionStart;
  const end = inputEl.current.selectionEnd;

  ... // other code within selection handler as per original

  // inputEl.current.focus();
  // // the line below doesn't work!
  // // inputEl.current.setSelectionRange(start + e.native.length, end + e.native.length)

  // //this one does, but is not good practice..
  // setTimeout(
  //   () =>
  //     inputEl.current.setSelectionRange(
  //       start + e.native.length,
  //       end + e.native.length
  //     ),
  //   10
  // );
  setSelection({start: start + e.native.length, end: end + e.native.length});
}
Run Code Online (Sandbox Code Playgroud)

模板更改为调用handleSelection()

<Picker
  set="emojione"
  onSelect={event => {
    handleSelection(event)
  }}
/>
Run Code Online (Sandbox Code Playgroud)

原始代码供参考

<Picker
  set="emojione"
  onSelect={e => {
    const start = inputEl.current.selectionStart;
    const end = inputEl.current.selectionEnd;
    //const result = domainString.substring(0, start) + e.native + domainString.substring(end, domainString.length)

    setDomainString(
      prevString =>
        prevString.substring(0, start) +
        e.native +
        prevString.substring(end, prevString.length)
    );

    setDomainsArray(
      domainEndings.map(
        ending =>
          domainString.substring(0, start) +
          e.native +
          domainString.substring(end, domainString.length) +
          ending
      )
    );

    inputEl.current.focus();
    // the line below doesn't work!
    // inputEl.current.setSelectionRange(start + e.native.length, end + e.native.length)

    //this one does, but is not good practice..
    setTimeout(
      () =>
        inputEl.current.setSelectionRange(
          start + e.native.length,
          end + e.native.length
        ),
      10
    );
  }}
/>
Run Code Online (Sandbox Code Playgroud)