最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - Is JavaScript's .setSelectionRange() incompatible with React Hooks? - Stack Overflow

programmeradmin6浏览0评论

This question revamps a question on r/reactjs.

I have a controlled input of which I can programmatically change the value. I would like to use .setSelectionRange() in order to maintain the caret position within the input.

Except this doesn't work: each re-render automatically sets the selection range to the end of the input by default.

The problem is illustrated in this sandbox, where the author of the original question fixed the problem with a 10ms setTimeout() delay.

How do I achieve this without using setTimeout() or getSnapshotBeforeUpdate(), which is not patible with Hooks?

This question revamps a question on r/reactjs.

I have a controlled input of which I can programmatically change the value. I would like to use .setSelectionRange() in order to maintain the caret position within the input.

Except this doesn't work: each re-render automatically sets the selection range to the end of the input by default.

The problem is illustrated in this sandbox, where the author of the original question fixed the problem with a 10ms setTimeout() delay.

How do I achieve this without using setTimeout() or getSnapshotBeforeUpdate(), which is not patible with Hooks?

Share Improve this question edited Nov 21, 2022 at 8:28 sideshowbarker 88.6k30 gold badges215 silver badges212 bronze badges asked Feb 8, 2020 at 18:03 user5902649user5902649 1
  • FYI that the link to the sandbox is dead now. – Atticus29 Commented Jun 22, 2023 at 20:48
Add a ment  | 

1 Answer 1

Reset to default 4

The basic problem as I see it is that .setSelectionRange() is being used in-line in the template, and should be wrapped in a useEffect().

I would also pull the selection handler out, just to be a bit tidier (as per handleDomainChange() and handleSubmit()).

useEffect for selection update

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

template change to call handleSelection()

<Picker
  set="emojione"
  onSelect={event => {
    handleSelection(event)
  }}
/>

Original code for reference

<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
    );
  }}
/>
发布评论

评论列表(0)

  1. 暂无评论