简体   繁体   中英

React setState renders component twice when called

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>
    )
  }
}

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 }};
  }

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>
    )
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM