shenku
shenku

Reputation: 12448

Can't access refs on ComponentDidMount

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

Upvotes: 8

Views: 7150

Answers (2)

Onza
Onza

Reputation: 1850

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.

Upvotes: 0

NoobSaibot
NoobSaibot

Reputation: 204

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/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 3

Related Questions