はじめてのReact #13「フォームと連動する」ラジオボタン編

Reactでフォームを使ってみようシリーズも佳境に差し迫った第4弾。今回はラジオボタンを触ってみます。

時限爆弾の解体ラジオボタンComponent

もはら映画で定番となった、時限爆弾の解体シーンで「青と赤どちらのコードを切るか…」を再現するComponentを作ってみたいと思います。3色のラジオボタンが並んでおりハズレを選択(コードを切断)すると爆発します。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>BomButton</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>

<h1>BomButton</h1>
<p>線を一つ選んで爆弾を解除してください</p>
<img src="./bom1.png">
<div id="root"></div>

<script type="text/babel">
class BomButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      radio: [
        {cd:'red',   name:"赤", checked:false},
        {cd:'blue',  name:"青", checked:false},
        {cd:'green', name:"緑", checked:false}
      ],
      lastclick: null  //最後に選択された色を入れる
    };

    // State内の色の中から抽選を行う
    let i = Math.floor(Math.random() * 100) % this.state.radio.length;
    this.hit = this.state.radio[i].cd;

    // 爆発時の素材を事前ロードしておく
    this._preload();

    // イベント用メソッド
    this.handleRadioClick = this.handleRadioClick.bind(this);
  }

  /**
   * 爆発時の素材を事前ロード
   */
  _preload(){
    this.se = new Audio('./bomb1.mp3');
    this.se.load();

    let img = new Image();
    img.src = "./bakuhatsu.png";
  }

  /**
   * ラジオボタンをクリック
   */
  handleRadioClick(e) {
    if( ! confirm('本当にこの線で良いですか?') ){
      return(false);
    }

    let name  = e.target.name;
    let value = this.state.radio.map( (item)=>{
      return({
        cd: item.cd,
        name: item.name,
        checked: (item.cd===name)? true:false
      })
    });

    this.setState({
        radio: value
      , lastclick: name
    });
  }

  render() {
    //----------------
    // 当選したら爆発
    //----------------
    if( this.hit === this.state.lastclick ){
      this.se.play();
      return <div><img src="./bakuhatsu.png" /></div>
    }
    //----------------
    // 選択肢の表示
    //----------------
    else{
      const radio = this.state.radio;
      const listItems = radio.map((r) =>
        <label key={r.cd.toString()} style={{color: r.cd}}>
          <input
            type="radio"
            name={r.cd}
            value={r.cd}
            checked={r.checked}
            onChange={this.handleRadioClick} />
          {r.name}<br />
        </label>
      );
      return <form>{listItems}</form>
    }
  }
}

ReactDOM.render(
  <BomButton />,
  document.getElementById('root')
);
</script>
</body>
</html>

実行結果

※爆発するとSEが再生されます。音量注意。

解説

ラジオボタンの生成方法

render()内の以下の箇所で生成していますが、基本的に前回のチェックボックスと同じですね。見た目をわかりやすくするためにCSSの指定を行っていますが、こちらは別の機会に解説したいと思います。

const listItems = radio.map((r) =>
  <label key={r.cd.toString()} style={{color: r.cd}}>
    <input
        type="radio"
        name={r.cd}
        value={r.cd}
        checked={r.checked}
        onChange={this.handleRadioClick} />
      {r.name}<br />
    </label>
  );

return <form>{listItems}</form>

チェックボックスと異なる点としては、ラジオボタンは1つしかcheckedtrueにしてはならないことです。handleRadioClick()メソッド内でState用データを作成する際の以下の箇所に注目。チェックされたnameが同じ値のときだけtrue、異なる場合はすべてfalseとしているのがわかると思います。

let value = this.state.radio.map( (item)=>{
      return({
        cd: item.cd,
        name: item.name,
        checked: (item.cd===name)? true:false
      })
});

書籍

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)
穴井 宏幸 石井 直矢 柴田 和祈 三宮 肇
翔泳社
売り上げランキング: 139,067

素材

www.irasutoya.com soundeffect-lab.info

参考ページ

reactjs.org