2

I'm having difficulty understanding why create-react-app is unable to compile, telling me that error 'updateWord' is not defined no-undef. I'm fairly new to React with ES6. Normally I would write a component like const App = React.createClass({ }); but I've decided to try out some syntactical sugar instead.

I have parent component App and a child component Input:

class App extends Component {
  constructor(props) {
    super(props);
    // all other code omitted...
  }

  handleInput(input) {
    console.log(`handle: ${input}`); 
    updateWord(input);
    // this also causes an error
    // this.updateWord(input);
  }

  updateWord(input) {
    console.log(`update ${input}`);
    // why isn't this defined?
  }

  render() {
    return (
      <div className="App">
        <Input onInput={this.handleInput} />
      </div>
      );
  }
}

class Input extends Component {
  handleInput(e) {
    let input = e.target.value;
    this.props.onInput(input);
  }

  render() {
    return (
      <form>
        <input onChange={this.handleInput.bind(this)} />
      </form>
      );
  }
}

I've tried changing to this.updateWord(input); instead of updateWord(input) but to no avail. I get:

"App.js:55 Uncaught TypeError: this.updateWord is not a function" 

Normally when I implement a similar pattern (with ES5) to what I'm doing now I have no difficulties. For example:

const App = React.createClass({
  getInitialState: function() {
    // all other code omitted...
  },

  handleInput: function(input) {
    console.log(`handle: ${input}`); 
    this.updateWord(input);
  },

  updateWord: function(input) {
    console.log(`update ${input}`);
    // In theory, this implementation would not cause any errors?
  },

  render: function() {
    return (
      <div className="App">
        <Input onInput={this.handleInput} />
      </div>
      );
  }
}
6
  • Sorry, still having an issues. I changed my method to updateWord and when I try to invoke the method as updateWord(input) I still get "error 'updateWord' is not defined no-undef" if I try to call it as this.updateWord(input) I get the error "App.js:55 Uncaught TypeError: this.updateWord is not a function" @AndrewLi My apologies, I'll update my question now.... Commented Oct 18, 2016 at 19:45
  • No, I think there was a misunderstanding on both parties :( I jumped the gun thinking your suggestion solved my problem. I still have this issue when renaming the update method. @AndrewLi Commented Oct 18, 2016 at 19:47
  • No problem @AndrewLi Commented Oct 18, 2016 at 19:50
  • @AndrewLi yes, I still get "App.js:61 Uncaught TypeError: this.updateWord is not a function" Commented Oct 18, 2016 at 19:53
  • I think I've got it. Use this.updateWord and then change onInput to {this.handleInput.bind(this)}. Commented Oct 18, 2016 at 20:02

1 Answer 1

2

The problem is that when you do this.updateWord(...) in this.handleInput, this refers to the Input component. Let me illustrate the problem:

When you set the onInput handler, like so:

onInput={this.handleInput}

Here, since your Input component is calling the function, this refers to the Input component. This is due to the line:

this.props.onInput(input);

The Input component is calling handleInput. That means, in your handleInput function, the this context is Input. Consider the line:

this.updateWord(input);

in the handleInput function. Here you call this.updateWord, but since this is Input, it tries to call updateWord from Input which does not exist, thus throwing the error.


The solution is to explicitly bind the this context as the class (App component) instead of the Input component, using either Function.prototype.bind or an arrow function. From the documentation:

The bind() method creates a new function that, when called, has its this keyword set to the provided value

You can apply it like so:

onInput={this.handleInput.bind(this)}

Or more preferably in the constructor:

this.handleInput = this.handleInput.bind(this);

With the second option you may then do:

onInput={this.handleInput}

(This is more preferable as binding in the render method will create a new function every time on render, which isn't preferred).

The this context in the line above is the class. Since you bind this, the class will be correctly used as this context in the function and executing this.updateWord will invoke the method in the class.

An even more preferable way is to use arrow functions instead of regular ES6 methods. From the documentation:

An arrow function expression has a shorter syntax compared to function expressions and does not bind its own this, arguments, super, or new.target.

We can apply this by assigning handleInput to an arrow function instead of a regular method:

handleInput = (input) => {
    console.log(`handle: ${input}`); 
    this.updateWord(input);
}

This will eliminate the use of bind completely and instead, use arrow functions. Since arrow functions don't bind their own this, it means this refers to the enclosing context. In the example above, a method is not used thus this refers to the class (the enclosing context). That will correctly call the class method updateWord, and consequently, you need not change the onInput event handler if you go this route.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.