0

I'm trying to make an interactive grid that, when you click on a cell, it increments a number on that specific cell only. Instead, when I click on a cell, the entire grid increments, and not the one cell.

I made React print out the ID of the button that was clicked. It only outputs one (correct) ID. I made sure that I passed a function () => {} instead of directly calling it. Trying to remove the arrow function results in React complaining that this is undefined.

I am aware that this may not be the best way (who am I kidding, this is the most horrible way) of making an interactive grid, but please bear with me:

  handleCellClick(id) {
    let cells = this.state.cells.slice()
    cells[id].level = cells[id].level + 1 // Increment ONLY ONE CELL
    console.log(cells[id]) // Logging to console only outputs one id, but it changes all of them?
    this.setState({ cells: cells });
  }

  render() {
    return (
      <div className="board">
        {
          Array(resolution).fill(null).map((_, x) => { // Represents the rows
            return (
              <div className='cellRow' key={x}>
                {
                  Array(resolution).fill(null).map((_, y) => { // Represents the columns
                    let id = (x * resolution) + y
                    return (
                      <Cell
                        key={id}
                        onClick={() => { this.handleCellClick(id) }} // This is where it binds the click event
                        data={this.state.cells[id]}
                      />
                    )
                  })
                }
              </div>
            )
          })
        }
        <p> {this.state.debug} </p>
      </div>
    )
  }

I expected that upon clicking one cell, by the code above, only that cell should increment, but instead, when actually testing it, it increments all the cells.

The array seems to be built properly.

Connecting to React DevTools shows that each cell has a different item in the array.

Here is the constructor function.

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.handleCellClick = this.handleCellClick.bind(this);
    this.state = {
      cells: Array(resolution * resolution).fill({
        level: 0,
        owner: null,
      }), // 5x5 for 25 in total
      turn: 'red'
    }
  }
  • Looks like when setting your state, you're setting the entire array variable to a new value, rather than a particular item in the array. – silencedogood Apr 11 at 13:56
  • Why are you attempting to use this in an arrow function? – DaveStSomeWhere Apr 11 at 13:57
  • Using this in arrow function will refer to the proper scope here, because arrows don't create context. Therefore it will refer to the class scope, and reference the methods properly. – silencedogood Apr 11 at 13:59
  • I based this on the Intro to React tutorial on reactjs.org. It uses this on arrow functions, so I thought it was fine. – EpicTonic Apr 11 at 14:00
  • 1
    @UladzislauUlasenka, console.log(cells[0] === cells[1] ? "What?" : "Nope :("); outputs What? which means that all the items in the array actually refer to only one object... so that's what's causing it :( – EpicTonic Apr 11 at 14:22
1

You are populating your state.cells with the same object so when you change id of one cell object it triggers others. Can you change your constructor like below and try

class Board extends React.Component {
   const cell = {
     level: 0,
     owner: null,
     turn: 'red'
   };
  constructor(props) {
    super(props);
    this.handleCellClick = this.handleCellClick.bind(this);
    this.state = {
      cells: Array(resolution * resolution).fill(Object.create(cell))
  }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.