1
votes

I'm trying to build a connect 4 game, which has a Board component comprised of 7 Column components all contain 6 Space components. Every Column has a Drop button above it, which will be used to drop the piece into the column. In order to alternate between "red" and "black" players, I have created a state "redOrBlue" which returns a boolean.

In a nutshell, when the Drop button is clicked, I want to toggle the value of "redOrBlue" to keep track of whose turn it is. I've set the onClick function to setState({redOrBlue: !this.state.redOrBlue)}, however, calling this function will cause react to render an extra column right below the column in which the button was clicked. I understand that setState automatically re-renders the DOM, but how can I keep from it rendering duplicates of a component? Thanks for your help!

class Column extends Component{
  constructor(){
    super();
    this.state = {
      myArray: [],
      buttonArray: [],
      buttonNumber: null,
      newArray: [],
      redOrBlue: true
    }
  }
  makeRow(){
    var component = <Space className="space"/>
    for(var i = 0; i < 6; i++){
      this.state.myArray.push(component);
    }
  }

  handleClick(){
    var num = this.props.colNumber;
    var array = this.props.boardArray[num];
    var boolean = false;
    var color;
    if(this.state.redOrBlue === true){
      color = "red"
    }else{
      color = "black"
    }
    for(var i = 5; i > -1; i--){
      if(boolean === false){
        if(array[i] === null){
          array[i] = color;
          boolean = true;
        }
      }
    }
    this.setState({redOrBlue: !this.state.redOrBlue})
    console.log(array)
  }

  render(){
    {this.makeRow()}
    return(
      <div className="column">
        <DropButton onClick={() => this.handleClick()} id={'button-' + this.props.colNumber} buttonNumber={this.props.colNumber} className={this.props.className}/>
        {this.state.myArray.map(function(component, key){
          return component
        })}
      </div>
    )
  }
}
2

2 Answers

1
votes

There are many things that you need to change in your code:

*First of all never store the ui items in state variable, state variable should contain only data and values.

*Never do any changes in state variable by this.state.a = '' or this.state.a.push({}), always treat the state values as immutable and use only setState to change the value.

*Always call function inside render that will create the ui part directly if you want to create something dynamically.

Call makeRow method from render and it will return the ui directly without storing it in state variable, like this:

makeRow(){
    var component = [];
    for(var i = 0; i < 6; i++){
      component.push(<Space key={i} className="space"/>);
    }
    return component;
 }

render(){
    return(
        <div className="column">
            <DropButton onClick={() => this.handleClick()} id={'button-' + this.props.colNumber}
                buttonNumber={this.props.colNumber} className={this.props.className}/>
            {this.makerow()}
        </div>
    )
}
0
votes

Remove {this.makeRow()} from your render function. All you're doing is adding another row to the state, in a rather non-kosher method, every time the component renders. Try something like this:

  constructor(){
    super();
    this.state = {
      myArray: [],
      buttonArray: [],
      buttonNumber: null,
      newArray: [],
      redOrBlue: true
    }
    this.makeRow();
  }
  makeRow(){
    var tempArray = [];
    var component = <Space className="space"/>
    for(var i = 0; i < 6; i++){
      tempArray.push(component);
    }
    this.setState({ myArray: tempArray }};
  }