2

My goal is to have the function handleClick() alert the squareNum for any Square Object clicked, as can be seen in the code below. Though implementation of such isn't working. At first, I tried implementing in accordance to this link , through binding - but no success.

After further research, I suspect the binding issue exists since upon click the square object created isn't identified, but rather the html rendered (the actual button).

As such I created the data-id attribute within the rendered button html that retrieves data from the square object. I proceeded to implement in accordance to this link.

Unfortunately, I get the error that getAttributes isn't defined, and not too sure what's wrong.

Along with the solution for the getAttributes issue, is there a better way to implement this, so the respective squareNum is identified upon click?

Thanks in advance

CODEPEN

CODE

class Square extends React.Component {
  //constructor
  constructor() {
    super();
    this.state = {
      value: null,
      squareNum: null,
    };
  }

  render() {
    return (
      //<button className="square" onClick = {() => this.setState({value: 'X'})}>
      //      <button className = "square" data-id = {this.props.squareNum} onClick = {() => this.props.onClick()}>
      <button className="square" data-id = {this.props.squareNum} onClick = {this.onClickSquare()}>
          {this.props.value}
      </button>
    );
  }

  onClickSquare() {
    this.props.onClick(this.props.squareNum);
  }
}

class Board extends React.Component {
  constructor() {
    super();
    this.state = {
      squares: Array(9).fill(null),
    };
  }

  renderRow(i) {
    var rowSquare = [];
    var square;

    //{objects.map((o, i) => <ObjectRow obj={o} key={i}/>}

    // create row of 3, creating square elements in array then return array
    for(var iterator = i; iterator < i+3; iterator++) {
      square = <Square key = {iterator} squareNum = {iterator} value = {this.state.squares[iterator]} onClick = {() => this.handleClick}  />;
      rowSquare.push(square);
    }
    return <div className ="board-row"> 
        {rowSquare}
    </div>
  } // end of renderRow()

  render() {
    const status = 'Next player: X';
    return (
      <div>
        <div className="status">{status}</div>
        {this.renderRow(0)}
        {this.renderRow(3)}
        {this.renderRow(6)}
      </div>
    );
  }

   handleClick(squareId) {
    // utilize slice to copy not change state
    // want to keep mutating changes to setState only
    const squares = this.state.squares.slice();
    alert(squareId);
    //squares[i] = 'X';
    this.setState({squares: squares});

  } // end of handleClick

}

class Game extends React.Component {
  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}
Community
  • 1
  • 1
H Co
  • 23
  • 2

1 Answers1

3

There are a few issues with your example, primarily that you're not passing props to the parent class. Furthermore, your onClick handlers require functions. ES6 arrow functions will implicitly bind the context, so you can wrap your handlers in an anonymous callback. As for the getAttributes error, my guess is that you were trying to query the DOM for that attribute, but you weren't selecting the element properly or keeping a reference to it. Regardless, the rest should be fairly straight forward and you wouldn't need to use the data-id attribute at all. See https://codepen.io/anon/pen/Kavmyz?editors=0010

class Square extends React.Component {
  //constructor
  constructor(props) {
    super(props);
    this.state = {
      value: null,
      squareNum: null,
    };
  }

  render() {
    return (
      <button className="square" onClick = {() => this.onClickSquare()}>
          {this.props.value}
      </button>
    );
  }

  onClickSquare() {
    this.props.onClick(this.props.squareNum);
  }
}

class Board extends React.Component {
  constructor() {
    super();
    this.state = {
      squares: Array(9).fill(null),
    };
  }

  renderRow(i) {
    var rowSquare = [];
    var square;

    // create row of 3, creating square elements in array then return array
    for(var iterator = i; iterator < i+3; iterator++) {
      square = ( <Square 
                 key={iterator}
                 squareNum={iterator}
                 value={this.state.squares[iterator]}
                 onClick={(squareNum) => this.handleClick(squareNum)}  />);
      rowSquare.push(square);
    }
    return <div className ="board-row"> 
        {rowSquare}
    </div>
  } // end of renderRow()

  render() {
    const status = 'Next player: X';
    return (
      <div>
        <div className="status">{status}</div>
        {this.renderRow(0)}
        {this.renderRow(3)}
        {this.renderRow(6)}
      </div>
    );
  }

   handleClick(squareId) {
    // utilize slice to copy not change state
    // want to keep mutating changes to setState only
    const squares = this.state.squares.slice();
    squares[squareId] = squareId;
    this.setState({squares: squares});

  } // end of handleClick
}

Hope this helps!

Max Sindwani
  • 1,267
  • 7
  • 15