Search code examples
reactjsevent-handlingstatesetstatekeyevent

Why is react repeatedly calling setState() when i call it with an eventHandler


I'm creating a Tetras game with react, all was working well and executing as expected till is added a Keyboard EventListener which majorly listens for the the Right and Left arrow key to move the component The Problem is, every time I call setState() in the function i assigned to the event it keeps repeating the same setState() function, without it even rendering the component, as a result i get the "Maximum update depth exceeded" Error. the code for my component:

class App extends React.Component {
  constructor () {
    super()
    this.state = {
      allBlocks: [],
      allBlockClass: []
    }
    setInterval(this.newBlock, 5000)

    document.addEventListener('keyup', this.userInput)
  }
  userInput = e => {
    switch (e.key) {
      case 'ArrowLeft': {
        this.moveLeft()
        break
      }
      case 'ArrowRight': {
//        this.moveRight()
        break
      }
      default: {
        console.log('either left or right key')
      }
    }
  }
    moveLeft = () => {
    this.setState(prevState => {
      const old_state = Object.assign([], prevState.allBlocks)
      let newBlocksClass = prevState.state.allBlockClass
      const new_state = old_state.map((blockData, index) => {
        if (blockData.active) {
          blockData.x_axis -= BLOCK_INCREMENT
          if (!(index > this.state.allBlockClass.length - 1)) {
            newBlocksClass[index].moveBlockLeft(BLOCK_INCREMENT)
          }
        }
        return blockData
      })
      return { allBlocks: new_state, allBlockClass: newBlocksClass }
    })
  }
  newBlock = () => {
    let positions = generateBlockPositions()
    const blockData = {
      positions: positions,
      active: true,
      x_axis: axis_props.Min_x_axis + BLOCK_INCREMENT * randomInt(3),
      y_axis: axis_props.Min_y_axis + BLOCK_INCREMENT
    }
    this.setState(prevState => {
      let new_state = [...prevState.allBlocks]
      new_state.push(blockData)
      return { allBlocks: new_state }
    })
  }
render () {
    this.resetValues()
    return (
      <div className='myBody'>
        {this.state.allBlocks.map((blockData, index) => {
          this.numberOfBlock++
          return (
            <CreateBlockStructure
              x_axis={blockData.x_axis}
              y_axis={blockData.y_axis}
              positions={blockData.positions}
              key={index}
              id={index}
              run={blockData.active}
              shouldRun={this.shouldRun}
              inActivate={this.inActivate}
              addBlockStructure={this.addBlockStructure}
            />
          )
        })}
        <button onClick={this.moveLeft}>Move left</button>
        <button onClick={this.moveLeft}>Move Right</button>
      </div>
    )
  }

You can checkout the full code for this project

The moveLeft() works properly outside the Keyevent function but when i call it with any other event handler it infinitly calls the setState() method e.g the MoveLeft button

the moveBlockLeft() is just a simple func in another component

moveBlockLeft(by){
    this.setState((prevState)=> {
      return {x_axis: prevState.x_axis - by}
    })
  }

Please i've really spent a lot of time debugging this bug, i have no idea what is going on Thanks in advance


Solution

  • you are trying to set a value of state variable while update value of another state variable which is why it is ending up into an infinite loop.

    Basically in your moveLeft function while setting state you are called moveBlockLeft which is again setting state.

    To fix this call moveBlockLeft outised the setState inside your moveLeft.