0

After having read about the bind requirement for methods to be bound to a React ES6 class, I am still having some difficulty with this example:

class ProductList extends React.Component {
  constructor(props) {
    super(props);
    this.state = { products: [] };
    this.updateState = this.updateState.bind(this);
  }

  componentDidMount() {
    this.updateState();
  }

  handleProductUpvote(productId) {
    Data.forEach((item) => {
      if (item.id === productId) {
        item.votes = item.votes + 1;
        return;
      }
    });
    this.updateState();
  }

  updateState() {
    const products = Data.sort((a,b) => {
      return b.votes - a.votes;
    });
    this.setState({ products });
  }

  render() {
    const products = this.state.products.map((product) => {
      return (
        <Product
          key={'product-' + product.id}
          id={product.id}
          title={product.title}
          description={product.description}
          url={product.url}
          votes={product.votes}
          submitter_avatar_url={product.submitter_avatar_url}
          product_image_url={product.product_image_url}
          onVote={this.handleProductUpvote}
        />
      );
    });
    return (
      <div className='ui items'>
        {products}
      </div>
    );
  }
}

class Product extends React.Component {
  constructor() {
    super();
    this.handleUpvote = this.handleUpvote.bind(this);
  }

  handleUpvote() {
    this.props.onVote(this.props.id);
  }

  render() {
    return (
      <div className='item'>
        <div className='image'>
          <img src={this.props.product_image_url} />
        </div>
        <div className='middle aligned content'>
          <div className='header'>
            <a onClick={this.handleUpvote}>
              <i className='large caret up icon'></i>
            </a>
            {this.props.votes}
          </div>
          <div className='description'>
            <a href={this.props.url}>
              {this.props.title}
            </a>
          </div>
          <div className='extra'>
            <span>Submitted by:</span>
            <img
              className='ui avatar image'
              src={this.props.submitter_avatar_url}
            />
          </div>
        </div>
      </div>
    );
  }
}
ReactDOM.render(
  <ProductList />,
  document.getElementById('content')
);

This returns

Uncaught TypeError: this.updateState is not a function(...) at handleProductUpvote

Is the initialized binding not sufficient in this case?

1
  • onVote prop on Product must have this bound to the component or else this will be incorrect. Commented Dec 15, 2016 at 22:31

1 Answer 1

2

Whenever you see this issue, you don't want to be adding the bind to the method that it's trying to call right then, but the method that you are inside of when the "this.xxx not defined" issue occurs.

Currently, it's getting the function handleProductUpvote just fine - but it's calling it in the wrong object context. So you need to do the same thing as you did with updateState in the constructor, but with that function. Though I have limited react knowledge I believe it's common to do that for every function that's used as an event listener or callback.

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

1 Comment

That was it. Makes perfect sense. Shoulda caught that.

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.