8

I am trying to select all the text in a textarea when a component loads using React v16.3.1

Following the Refs docs I have a basic sample but this.textarea is always undefined, if I change this sample to execute the same code on a button click it works fine.

So whats going on? I had expected that after mounting the component should be available?

Sample code:

import React from "react";

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.textarea = React.createRef();
  }

  componentDidMount = () => {
    this.textarea.current.select();
  };

  render() {
    return (
      <div>
        <textarea
          className="form-control"
          defaultValue="the quick brown fox."
          ref={this.textarea}
        />
      </div>
    );
  }
}

From package.json:

"react": "^16.3.1",
"react-dom": "^16.3.1",

Thanks

shenku
  • 11,969
  • 12
  • 64
  • 118
  • It's working correctly for me, are you sure you have the correct version of react? – Axnyff Apr 15 '18 at 07:34
  • What if you change arrow function? `componentDidMount() { this.textarea.current.select(); };` – Tomasz Mularczyk Apr 15 '18 at 07:37
  • @Axnyff yup, 16.3.1 is the version installed... – shenku Apr 15 '18 at 09:23
  • @TomaszMularczyk no change – shenku Apr 15 '18 at 09:30
  • @shenku I have the same issue, did you find out what was wrong? I'm using 16.8.2 – HRK44 Feb 18 '19 at 13:21
  • Ok I fixed my code using this answer https://stackoverflow.com/questions/44074747/componentdidmount-called-before-ref-callback/50019873#50019873 Basically, ref will be null if the component is not rendered (for example if you return a loading item or something like that). So I just use hide/show logic instead of not returning my referenced element. – HRK44 Feb 18 '19 at 16:48

2 Answers2

3

The documentation says:

Note

The examples below have been updated to use the React.createRef() API introduced in React 16.3. If you are using an earlier release of React, we recommend using callback refs instead.

If you are using an earlier release of React, u need use callback refs

class App extends React.Component {
  constructor(props) {
    super(props);
    this.textarea = null;

    this.setTextareaRef = element => {
      this.textarea = element;
    };
  }

  componentDidMount = () => {
    if (this.textarea) this.textarea.select();
  };

  render() {
    return (
      <div>
        <textarea
          className="form-control"
          defaultValue="the quick brown fox."
          ref={this.setTextareaRef}
        />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Using React 16.3

class App extends React.Component {
  constructor(props) {
    super(props);
    this.textarea = React.createRef();
  }

  componentDidMount = () => {
    this.textarea.current.select();
  };

  render() {
    return (
      <div>
        <textarea
          className="form-control"
          defaultValue="the quick brown fox."
          ref={this.textarea}
        />
      </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://unpkg.com/react@16.3.1/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.3.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Community
  • 1
  • 1
NoobSaibot
  • 204
  • 1
  • 2
  • 10
  • okay some progress, if I remove all my existing application logic and use as above it works, but when I use it with react-router etc. it just doesn't work - can't figure this one out, but you are correct it works as a simple component. – shenku Apr 15 '18 at 09:49
  • Tested using **react-router-dom**: https://stackblitz.com/edit/react-hmasqk?file=index.js – NoobSaibot Apr 15 '18 at 11:32
0

For someone experiencing the same issue make sure that there's not another error somewhere in the stack that is causing a parent component to catch, for example.

class MyComponent extends React.Component {
  private myRef = React.createRef();
  constructor(props) {
    super(props);
  }
  componentDidMount() {
    console.log(this.myRef.current);
  }
  render() {
    return (<div ref={this.myRef} />);
  }
}

You may think that this.myRef will always be defined in this case but as it stands say you are using it like this

<SomeNewComponent>
  <MyComponent />
</SomeNewComponent>

And some new component has an error of some sort during mounting... specially if it handles componentDidCatch and componentDidMount it will then make MyComponent's div never truly render.

It is odd that componentDidMount would be firing, but apparently it does; it's easy to overlook the stacktrace because all you see is the error from the myRef being null while the other one is way above in the console.

Onza
  • 1,710
  • 4
  • 18
  • 31