miga89
miga89

Reputation: 438

React.Js: Prevent onClick-events of siblings while a onClick is executed

what's the best practice for the following problem?

I have a parent component (Board) and four child components (GameButton) which are buttons that one can click on. The button click illuminates the button for 0,5s and then turns the button back to the initial style. I want to prevent any clicks on other buttons while one button is illuminated.

class GameButton extends React.Component {
    constructor(){
    super();
    this.state = {
      illuminated:false,
    };
    this.playButton = this.playButton.bind(this);
  }

  playButton(){

    if (this.props.gameActive && this.props.clickable){
      // lock all other buttons until this function is done executing
      this.props.toggleButtonAccess();
      console.log(this.props.clickable);

      // pass the second state update in a callback function to ensure delayed execution
      this.setState({illuminated:true}, function() {
        // arrow function to prevent binding of this to window
      window.setTimeout(() => {
        this.setState({illuminated: false});
        this.props.toggleButtonAccess();
        console.log(this.props.clickable);

      },500);
      });
    }

  }
  render() {
    var buttonStyle = this.props.colorClass; 
    if (this.state.illuminated){
      buttonStyle += " illuminated-" + this.props.color;
      console.log(buttonStyle);
    }
    return (
        <div className={buttonStyle} onClick={this.playButton}></div>
    );
  }
}


class Board extends React.Component {
  constructor(){
    super();
    this.state = {
      gameActive:true,
    };
    this.toggleButtonAccess = this.toggleButtonAccess.bind(this);
  }

  toggleButtonAccess(){
    this.clickable = (this.clickable) ? false : true;
  }

  render() {
    return (
      <div className ="game-container">
       <GameButton colorClass="game-btn green" color="green" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>
        <GameButton colorClass="game-btn red" color="red" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>
       <GameButton colorClass="game-btn yellow" color="yellow" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>
       <GameButton colorClass="game-btn blue" color="blue" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>

      </div>
    );
  }
}

So far I tried to do it by having the property this.clickable on the parent (Board) component and then toggle it on and off every time a button is clicked with toggleAccessButton(). However, this is not working. Is there a better way to do this?

Upvotes: 0

Views: 1208

Answers (2)

Tharaka Wijebandara
Tharaka Wijebandara

Reputation: 8065

There are few things that you need to change. First, in GameButton, I don't see value in gameActive. In fact, you don't pass any value to this prop. If it doesn't have anything other purpose which you haven't mentioned here, simply remove it.

class GameButton extends React.Component {
    constructor(){
    super();
    this.state = {
      illuminated:false,
    };
    this.playButton = this.playButton.bind(this);
  }

  playButton(){

    if (this.props.clickable){
      // lock all other buttons until this function is done executing
      this.props.toggleButtonAccess();
      console.log(this.props.clickable);

      // pass the second state update in a callback function to ensure delayed execution
      this.setState({illuminated:true}, function() {
        // arrow function to prevent binding of this to window
      window.setTimeout(() => {
        this.setState({illuminated: false});
        this.props.toggleButtonAccess();
        console.log(this.props.clickable);

      },500);
      });
    }

  }
  render() {
    var buttonStyle = this.props.colorClass; 
    if (this.state.illuminated){
      buttonStyle += " illuminated-" + this.props.color;
      console.log(buttonStyle);
    }
    return (
        <div className={buttonStyle} onClick={this.playButton}></div>
    );
  }
}

Now in Board component, you need to keep clickable in you state and use setState to change it in toggleButtonAccess. Here also I don't understand why you have gameActive in the state.

class Board extends React.Component {
  constructor(){
    super();
    this.state = {
      gameActive:true,
      clickable:true
    };
    this.toggleButtonAccess = this.toggleButtonAccess.bind(this);
  }

  toggleButtonAccess(){
    this.setState({clickable:!this.state.clickable});
  }

  render() {
    return (
      <div className ="game-container">
       <GameButton colorClass="game-btn green" color="green" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>
        <GameButton colorClass="game-btn red" color="red" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>
       <GameButton colorClass="game-btn yellow" color="yellow" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>
       <GameButton colorClass="game-btn blue" color="blue" clickable={this.clickable} toggleButtonAccess={this.toggleButtonAccess}/>
      </div>
    );
  }
}

Assuming that you have setup other things correctly, it should work now as expected.

Upvotes: 1

Taras
Taras

Reputation: 36

i would prefer to use React state. In my example i toggle cssClass.
Here code example:

    const ButtonComponent = ({className,handler,idx}) => {
     return ( < div className = {
      className
    } >
    < button onClick = {
      handler
    } > {
      idx
    } < /button> < /div >);
};

class HolderComponent extends React.Component {
    constructor() {
      super();
      this.state = {
        current: null
      }

    }

    handler(e, idx) {
      e.preventDefault();
      console.log(idx + "CURRENT");
      this.setState({
        current: idx
      }, () => {
        const self = this;
        setTimeout(() => {
          self.setState({
            current: null
          });
        }, 1000)
      });
    }

    getButtons(props = []) {

      return props.map((btn, idx) => this.getButton(idx));

    }

    getButton(idx) {

        return ( < ButtonComponent key = {
            idx + "uniqKey"
          }
          className = {
            this.state.current === idx ? "test" : "testActive"
          }
          handler = {
            (e) => this.handler(e, idx)
          }
          isActive = {
            this.state.current === idx
          }
          idx = {
            idx
          }
          />)
        }

        render() {
          return ( < div > {
              this.getButtons([1, 2, 3, 4])
            } < /div>);

          }

        }

        ReactDOM.render( < HolderComponent / > , document.getElementById("app"));

Upvotes: 1

Related Questions