AnnaFractuous
AnnaFractuous

Reputation: 109

React onClick fires multiple times on load and doesn't contain callback function in component props

I believe I have two basic problems, which are probably connected. I'm trying to place an event handler with a callback function on a nested component. It didn't seem to be doing anything, so I replaced the callback function with an alert of JSON.stringify(this.props) to see if that would shed any light. It illuminated two problems: 1) my callback function was not in the props. 2) the alert popped up 2 times on page load, but did not pop up on click, like it was supposed to. I'm working through this React tutorial. Here are the relevant components:

var App = React.createClass({
  mixins: [Catalyst.LinkedStateMixin],
  getInitialState: function(){
    return {
      fishes: {},
      order: {}
    }
  },
  componentDidMount: function(){
    base.syncState(this.props.params.storeId + '/fishes', {
      context: this,
      state: 'fishes'
    });
    var localStorageRef = localStorage.getItem('order-' + this.props.params.storeId);
    if(localStorageRef){
      this.setState({
        order: JSON.parse(localStorageRef)
      });
    }
  },
  componentWillUpdate: function(nextProps, nextState){
    localStorage.setItem('order-' + this.props.params.storeId, JSON.stringify(nextState.order));
  },
  loadSamples: function(){
    this.setState({
      fishes: require('./sample-fishes.js')
    });
  },
  addFish: function(fish){
    var timestamp = (new Date()).getTime();
    this.state.fishes['fish-' + timestamp] = fish;
    this.setState({ fishes: this.state.fishes });
  },
  removeFish: function(key){
    if(confirm("Are you sure you want to remove this fish?")){
      this.state.fishes[key] = null;
      this.setState({ fishes: this.state.fishes });
    }
  },
  addToOrder: function(key){
    this.state.order[key] = this.state.order[key] + 1 || 1;
    this.setState({ order: this.state.order });
  },
// <<<<<<<< the function I'm having trouble with >>>>>>>>
  removeFromOrder: function(key){  
    alert('hi');
    delete this.state.order[key];
    this.setState({ order: this.state.order });
  },
  renderFish(key){
    return <Fish key={key} index={key} details={this.state.fishes[key]} addToOrder={this.addToOrder}/>
  },
  render: function(){
    return (
      <div className="catch-of-the-day">
        <div className="menu">
          <Header tagline="Fresh Seafood Market"/>
          <ul className="list-of-fish">
            {/*{ Object.keys(this.state.fishes).map(this.renderFish) }*/}
            { Object.keys(this.state.fishes).length > 0 ? Object.keys(this.state.fishes).map(this.renderFish) : <li>No Fishes!</li> }
          </ul>
        </div>
// <<<<<<<< I pass the function through to the Order component >>>>>>>>
        <Order fishes={this.state.fishes} order={this.state.order} removeFromOrder={this.removeFromOrder}/>
        <Inventory fishes={this.state.fishes} addFish={this.addFish} removeFish={this.removeFish} loadSamples={this.loadSamples} linkState={this.linkState}/>
      </div>
    )
  }
});

var Order = React.createClass({
  renderOrder: function(key){
    var fish = this.props.fishes[key];
    var count = this.props.order[key];
// <<<<<<<< the onClick I'm having trouble with >>>>>>>>
    var removeButton = <button onCLick={this.props.removeFromOrder.bind(null, key)}>&times;</button>
    // var removeButton = <button onCLick={alert(JSON.stringify(this.props))}>&times;</button>
    if(!fish) {
      return <li key={key}>Sorry, that fish is no longer available! {removeButton}</li>
      // return <li key={key}>Sorry, that fish is no longer available!</li>
    }
    return (
      <li key={key}>
        {count}lbs
        {" " + fish.name}
        <span className="price">{helpers.formatPrice(count * fish.price)} {removeButton}</span>
        {/*<span className="price">{helpers.formatPrice(count * fish.price)}</span>*/}
      </li>
    )
  },
  render: function(){
    var orderIds = Object.keys(this.props.order);
    var total = orderIds.reduce((prevTotal, key)=>{
      var fish = this.props.fishes[key];
      var count = this.props.order[key];
      var isAvailable = fish && fish.status === 'available';
      if(isAvailable) {
        return prevTotal + (count * parseInt(fish.price) || 0);
      }
      return prevTotal;
    }, 0);
    return (
      <div className="order-wrap">
        <h2 className="order-title">Your Order</h2>
        <ul className="order">
          { orderIds.length > 0 ? orderIds.map(this.renderOrder) : ""}
          <li className="total">
            <strong>Total:</strong>
            {helpers.formatPrice(total)}
          </li>
        </ul>
      </div>
    )
  }
});

The props for Order should include: the available fishes with all of their details, the current order with a fish id and quantity, and the removeFromOrder callback. When I explore the component in React dev tools, it has all of these things.

When I replace the removeFromOrder callback with an alert of the props, what happens is: - on click, nothing - on page refresh, two alerts pop up: the props in the first include the current order and an empty fishes array, the props in the second include the current order and the populated fishes array. Neither show the removeFromOrder callback function, which appears to be undefined from the perspective of the event listener.

On a potentially related note, when I explore the component in React dev tools and hover over a list item in the Order, I get the following error: TypeError: node.getBoundingClientRect is not a function. I'm not sure if this is part of my problem; if it's not, I'm not too concerned about it, since it only seems to pop up when I hover over the element in dev tools.

Thank you for reading this long thing, and any help would be much appreciated!

Upvotes: 2

Views: 13351

Answers (1)

AnnaFractuous
AnnaFractuous

Reputation: 109

As @azium pointed out, the problem was a simple typo: onCLick={alert()} should instead be onClick={() => alert()}. Facepalm.

Upvotes: 4

Related Questions