web-dev-qa-db-fra.com

Modifiez la position du curseur dans une zone de texte avec React

J'ai une zone de texte dans React que je veux transformer en un "bloc-notes". Ce qui signifie que je veux que la touche "tab" soit en retrait au lieu de se défaire. J'ai regardé cette réponse , mais je n'arrive pas à le faire fonctionner avec React. Voici mon code:

handleKeyDown(event) {
    if (event.keyCode === 9) { // tab was pressed
        event.preventDefault();
        var val = this.state.scriptString,
            start = event.target.selectionStart,
            end = event.target.selectionEnd;

        this.setState({"scriptString": val.substring(0, start) + '\t' + val.substring(end)});
        // This line doesn't work. The caret position is always at the end of the line
        this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1;
    }
}
onScriptChange(event) {
   this.setState({scriptString: event.target.value});
}
render() {
    return (
        <textarea rows="30" cols="100" 
                  ref="input"
                  onKeyDown={this.handleKeyDown.bind(this)}
                  onChange={this.onScriptChange.bind(this)} 
                  value={this.state.scriptString}/>
    )
}

Lorsque j'exécute ce code, même si j'appuie sur la touche "tab" au milieu de la chaîne, mon curseur apparaît toujours à la fin de la chaîne. Quelqu'un sait comment régler correctement la position du curseur?

14
Discombobulous

Vous devez changer la position du curseur après l'état a été mis à jour (setState() ne mute pas immédiatement this.state)

Pour ce faire, vous devez envelopper this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1; dans une fonction et passez-le comme deuxième argument à setState (rappel).

handleKeyDown(event) {
      if (event.keyCode === 9) { // tab was pressed
          event.preventDefault();
          var val = this.state.scriptString,
          start = event.target.selectionStart,
          end = event.target.selectionEnd;
          this.setState(
              {
                  "scriptString": val.substring(0, start) + '\t' + val.substring(end)
              },
              () => {
                  this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1
              });
      }
 }

jsfiddle

23
QoP

Voici une solution dans une architecture de type crochets. Ma recommandation est de changer la zone de texte value et selectionStart immédiatement lors de l'insertion des onglets.

import React, { useRef } from "react"

const CodeTextArea = ({ onChange, value, error }) => {
  const textArea = useRef()
  return (
      <textarea
        ref={textArea}
        onKeyDown={e => {
          if (e.key === "Tab") {
            e.preventDefault()

            const { selectionStart, selectionEnd } = e.target

            const newValue =
              value.substring(0, selectionStart) +
              "  " +
              value.substring(selectionEnd)

            onChange(newValue)
            if (textArea.current) {
              textArea.current.value = newValue
              textArea.current.selectionStart = textArea.current.selectionEnd =
                selectionStart + 2
            }
          }
        }}
        onChange={e => onChange(e.target.value)}
        value={value}
      />
  )
}
0
seveibar