1

i'm setting a textarea's selectionRange using a react ref, but that contradicts with the react "way" .

the ref is working perfectly .

i want to set the textarea's selectionRange using state, not a ref ( like in this code snippet )

import React, { Component } from 'react';

export default class App extends Component {

  constructor() {
    super();

    this.state = {
      value:"some placeholder text",
    }

    this.textareaRef = React.createRef();
  }

  handleChange=({ target : { value : text } })=>{
   this.setState({value : text});
  };

  componentDidMount(){
    // setting the cursor position to the end of text on mount .
    const textareaObj = textareaRef.current;
    const cursor_pos = this.state.value.length;

    textareaObj.setSelectionRange(cursor_pos , cursor_pos );
    textareaObj.focus();
  }

  render(){
    return (
    <textarea 
      value={this.state.value} 
      onChange={this.handleChange}
      ref={textareaRef}>
    </textarea>
    );
  }
}
soufiane yakoubi
  • 861
  • 11
  • 31

3 Answers3

1

You're looking to create a "controlled" component, as in React should control the state of the element rather than the DOM.

You're half way there, just a couple of tweaks: https://jsfiddle.net/tombarton/2s5q398c/3/

class TextArea extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
        inputValue: 'Here is some content...'
    }
    this.onTextareaChange = this.onTextareaChange.bind(this);
  }

  onTextareaChange(e) {
    const { value } = e.target;
    this.setState(state => ({
        ...state,
      inputValue: value,
    }))
  }

  render() {
    return (
      <div>
        <textarea value={this.state.inputValue} onChange={this.onTextareaChange} />
      </div>
    )
  }
}

ReactDOM.render(<TextArea />, document.querySelector("#app"))

As you can see, we assign a value from the component's state to the value attribute of the textarea element. To complete the process, we assign a method to update the state to the onChange attribute of the element. When the onChange event fires, it is passed into our method, we extract the updated value and update our component's state. As we're passing our method outside of the class, we have to bind this to it, as you can see in the constructor.

Hope that helps.

tombraider
  • 1,127
  • 1
  • 11
  • 19
  • you misunderstood my problem , the problem is setting the textarea's selectionRange using State , not the inputvalue . i edited the post . – soufiane yakoubi Feb 11 '19 at 11:09
  • Ah I see. In that case, why do you think a ref isn't suitable here? You're calling a method on an element, the ref is just used to easily identify the element. – tombraider Feb 11 '19 at 11:18
  • yes i agree, i'm using it currently , but i just want to have one single source of truth . i mean setting one state in the App parent component and then passing its properties to the children . – soufiane yakoubi Feb 11 '19 at 11:21
1

I see it like this:

export default class App extends Component {

  constructor() {
    super();

    this.state = {
      value:"some placeholder text",
    }
  }

  handleChange=({ target : { value : text } })=>{
    this.setState({value : text});
  };

  render(){
     return (
       <textarea 
         value={this.state.value} 
         onChange={this.handleChange}
         ref={this.setCursorPos}>
       </textarea>
    );
  }

  setCursorPos = (textarea) => {
    const cursor_pos = this.state.value.length;

    textarea.setSelectionRange(cursor_pos , cursor_pos );
    textarea.focus();
  }
}
Alexey Baguk
  • 168
  • 1
  • 6
1

What you want to do is to set selectionStart and selectionEnd textarea properties using state without using refs? Something like this:

render(){
  const { value } = this.state;
  const startingPoint = 0;
  return (
    <textarea 
      value={this.state.value} 
      selectionStart={startingPoint}
      selectionEnd={startingPoint + value.length}
    </textarea>
  );
}

But currently it's not possible, you can take a look at all <textarea>'s properties here. So using refs in this case is perfectly fine.

dporechny
  • 638
  • 5
  • 12