2

Why is this.refs undefined in the code below?

class NewItem extends React.Component {
  handleClick() {
    console.log(this.refs) //prints out undefined
    const name = this.refs.name.value;
    const description = this.refs.description.value;
    $.ajax({
      url: 'api/v1/items',
      type: 'POST',
      data: {
        item: {
          name: name,
          description: description
        }
      },
      success: (item) => {
        this.props.handleSubmit(item);
      }
    });
  }

  render() {
    return (
      <div>
        <input ref='name' placeholder='Enter the name of the item' />
        <input ref='description' placeholder='Enter the description of the item' />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    )
  }
}
monk
  • 87
  • 1
  • 3
  • 10

3 Answers3

8

The method is not bound to this when used as a function:

<button onClick={this.handleClick.bind(this)}>Submit</button>

or

<button onClick={event => this.handleClick(event)}>Submit</button>

or bind it in constructor:

constructor(props, context) {
   super(props, context);

   this.handleClick = this.handleClick.bind(this);
}
Sulthan
  • 128,090
  • 22
  • 218
  • 270
1

You need to bind this to your handleClick() function, like this:

<button onClick={this.handleClick.bind(this)}>Submit</button>

or through the constructor, like this:

constructor(props) {
  ...
  this.handleClick = this.handleClick.bind(this);
}

Though you should avoid using string literals in refs. This approach is deprecated.

Legacy API: String Refs

If you worked with React before, you might be familiar with an older API where the ref attribute is a string, like "textInput", and the DOM node is accessed as this.refs.textInput. We advise against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases. If you're currently using this.refs.textInput to access refs, we recommend the callback pattern instead.

Instead do:

constructor(props) {
  this.nameInputRef;
  this.descriptionInputRef;
}

...

  <input ref={(el) => {this.nameInputRef = el;} placeholder='Enter the name of the item' />
  <input ref={(el) => {this.descriptionInputRef = el;} placeholder='Enter the description of the item' />
Community
  • 1
  • 1
Chris
  • 57,622
  • 19
  • 111
  • 137
0

You havens bind the functions. it should be done like this

class NewItem extends React.Component {

    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }

  handleClick() {
    console.log(this.refs) //prints out undefined
    const name = this.refs.name.value;
    const description = this.refs.description.value;
    $.ajax({
      url: 'api/v1/items',
      type: 'POST',
      data: {
        item: {
          name: name,
          description: description
        }
      },
      success: (item) => {
        this.props.handleSubmit(item);
      }
    });
  }

  render() {
    return (
      <div>
        <input ref='name' placeholder='Enter the name of the item' />
        <input ref='description' placeholder='Enter the description of the item' />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    )
  }
}

Try using the ES6 features, The arrow functions to avoid this binding issue. like this.

  handleClick =()=> {
    console.log(this.refs) //prints out undefined
    const name = this.refs.name.value;
    const description = this.refs.description.value;
    $.ajax({
      url: 'api/v1/items',
      type: 'POST',
      data: {
        item: {
          name: name,
          description: description
        }
      },
      success: (item) => {
        this.props.handleSubmit(item);
      }
    });
  }
TRomesh
  • 4,323
  • 8
  • 44
  • 74