はじめてのReact #12「フォームと連動する」チェックボックス編

Reactでフォームを使ってみようシリーズも第3弾。今回のテーマはチェックボックスです。

都道府県チェックボックスComponent

よく見かける都道府県一覧のチェックボックスです。例えば「中国地方」をチェックすると、島根や鳥取など中国地方全体にチェックが入る機能もついてます。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>AreaCheck</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>

<div id="root"></div>

<script type="text/babel">
  class AreaCheck extends React.Component {
    constructor(props) {
      super(props);
      this.cd = this.props.cd;
      this.state = {
        area: this.props.area
      };

      this.handleKenChange = this.handleKenChange.bind(this);  //各都道府県のチェック
      this.handleAllChange = this.handleAllChange.bind(this);  //まとめてチェック
    }

    /**
     * 各都道府県をチェック
     */
    handleKenChange(e) {
      let name  = e.target.name;
      let value = this.state.area.map( (ken)=>{
        return({
          cd: ken.cd,
          name: ken.name,
          checked: (ken.cd===name)? e.target.checked:ken.checked
        })
      });

      this.setState({area: value});
    }

    /**
     * まとめてチェック
     */
    handleAllChange(e){
      let value = this.state.area.map( (ken)=>{
        return({
          cd: ken.cd,
          name: ken.name,
          checked: e.target.checked
        })
      });

      this.setState({area: value});
    }

    render() {
      let area = this.state.area;
      let listItems = area.map((ken) =>
        <label key={ken.cd.toString()}>
          <input
            type="checkbox"
            name={ken.cd}
            value={ken.cd}
            checked={ken.checked}
            onChange={this.handleKenChange} />
            {ken.name}
        </label>
      );

      return (
        <form>
          <label key={this.cd}>
            <input type="checkbox" name="ALL" onChange={this.handleAllChange} />
            このエリアをチェック
          </label>
          <br />
          {listItems}
        </form>
      );
    }
  }

  const Chugoku = [
    {cd:'31', name:'鳥取県', checked:false},
    {cd:'32', name:'島根県', checked:false},
    {cd:'33', name:'岡山県', checked:false},
    {cd:'34', name:'広島県', checked:false},
    {cd:'35', name:'山口県', checked:false}
  ];
  const Shikoku =[
    {cd:'36', name:'徳島県', checked:false},
    {cd:'37', name:'香川県', checked:false},
    {cd:'38', name:'愛媛県', checked:false},
    {cd:'39', name:'高知県', checked:false}
  ];
  ReactDOM.render(
    <div>
      <AreaCheck cd="CG" area={Chugoku} />
      <AreaCheck cd="SK" area={Shikoku} />
    </div>,
    document.getElementById('root')
  );
</script>
</body>
</html>

実行結果

中国地方と四国地方のチェックボックスが表示されます。まとめてチェックする箇所がありますが、中国と四国でそれぞれ独立して動いているのが確認できます。 https://youtu.be/19_TCatnYkU

解説

デフォルトでチェックしたい

checked属性にBooleanの値を渡すだけです。trueならチェックする、falseならチェックしないという挙動になります。

<input type="checkbox" checked={true} />

チェック状態を確認したい

イベント発生時に呼び出されるメソッドなどで、以下のようにe.target.checkedを参照することでチェック状態を取得することができます。trueならチェックされている、falseならチェックが外れています。

constructor() {
  this.handleItemChange = this.handleItemChange.bind(this);
}

handleItemChange(e){
  let ischeck = e.target.checked;
}

render() {
  return( <input type="checkbox" onChange={this.handleItemChange} /> );
}

注意点

Stateは最初の階層だけ

以下のようにStateを初期化していますが、

  constructor(props) {
    super(props);
    this.state = {
      area: this.props.area
    };

これは最終的に以下のような連想配列になります。

{
  area: [
    {cd:'31', name:'鳥取県', checked:false},
    {cd:'32', name:'島根県', checked:false},
    /* 略 */
  ]
}

これまでthis.setState()は賢いので、仮に以下のようなコードを書いたとしても、areaはそのままに新たにfooを追加するという挙動になります。

this.setState({foo:"bar"});

ところが、こういった挙動をしてくれるのは今回のケースだと最初の階層だけです。例えば島根県checkedだけ変更したいと思い、以下のようなコードを書いてしまうと、他の都道府県がすべて消え去ってしまいます。

this.setState({area:[{cd:'32', name:'島根県', checked:true}]});

そういった事情からここでは以下のようにすべての都道府県を作成する処理を行っています。

handleAllChange(e){
  let value = this.state.area.map( (ken)=>{
    return({
      cd: ken.cd,
      name: ken.name,
      checked: e.target.checked
    })
  });

  this.setState({area: value});
}

書籍

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

参考ページ

reactjs.org