はじめてのReact #11「フォームと連動する」プルダウン編

前回のテキストボックスに続き、今回はプルダウン(<select>)の操作方法になります。 ほぼ同じやり方で<ul>,

    などのリストにも適用できます。

    メンバーリストComponent

    メンバーの一覧をプルダウンとして表示し、名前をクリックすると削除されるComponentです。公式ドキュメントの「Lists and Keys」のサンプルコードを参考にしています。

    サンプル

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8" />
      <title>MemberList</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 MemberList extends React.Component {
         constructor(props) {
           super(props);
           this.state = {
              guys: props.guys
           };
    
           this.handleRemove = this.handleRemove.bind(this);
         }
    
         handleRemove(id){
           const guys = this.state.guys;
           const new_guys = [];
    
             for( let i=0; i<guys.length; i++ ){
               if(guys[i].id !== id){
                 new_guys.push(guys[i]);
               }
           }
    
           this.setState({guys:new_guys});
         }
    
         render(){
           const guys = this.state.guys;
           const listItems = guys.map((guy) =>
             <option key={guy.id.toString()} onClick={ ()=>{ this.handleRemove(guy.id) } }>
               {guy.name}
             </option>
           );
    
           return <select size="3">{listItems}</select>;
         }
       }
    
    
        const guys = [
          {id:10, name:"Foo"},
          {id:20, name:"Bar"},
          {id:30, name:"Hoge"}
        ];
    
        ReactDOM.render(
          <MemberList guys={guys} />,
          document.getElementById('root')
        );
      </script>
    </body>
    </html>
    

    実行結果

    プルダウンはブラウザの拡大機能で大きくしています。クリックするとその要素が削除されるのがわかります。 https://youtu.be/mQZHZr8tSTo

    解説

    要素を作成する

    mapメソッドで要素を作成する

    render()内でプルダウンを作成しています。ここではReactではなくJavaScriptに搭載されているmapメソッドで配列の要素に対し順番に処理を施し、最終結果を変数listItemsへ返却しています。

      const listItems = guys.map((guy) =>
        <option key={guy.id.toString()} onClick={ ()=>{ this.handleRemove(guy.id) } }>
          {guy.name}
        </option>
      );
    

    この配列に対しmapで処理を回していくのは、React使いの間ではよくやる手法だそうです。 わかりづらい場合は簡単なサンプルを実行してみると良いかと思います。

    let array1 = [1,2,3];
    let map1   = array1.map( (x)=>{ return( x + 1 ) } );
    
    console.log(map1); // [2, 3, 4] が出力される
    

    key属性

    ここで新たにkey属性が登場しています。これはリストなどの複数の要素を作成する際にReactが要求してくる物で、各要素で重複しないユニークな値をつけてあげる必要があります。付けない場合は実行時エラーとなります。

    react.development.js:316 Warning: Each child in an array or iterator should have a unique "key" prop.

    Reactは内部でこのkeyを参照しながら、render()時に最小限の更新となるよう工夫をしているようです。パフォーマンスに関わるところだったんですね。

    初期値を変更する

    HTMLで言うところのselectedの値を指定するには、render()で返却しているJSXの<select>要素にdefaultValue属性を記述します。以下のようにすると2番目の要素が選択された状態で表示されます。

      return <select size="3" defaultValue="Bar">{listItems}</select>;
    

    選択されている値を動的に管理する

    前述のdefaultValueは動的に管理しないプルダウンの初期値を設定する物です。Stateなどに入れて管理する場合はvalueを使用します。以下のコードではPropsからもらってきた値をStateに入れ、プルダウンの値が変更されたらonChangeイベントで拾ってStateの更新を行っています。

    class MemberList extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
            guys: this.props.guys,
           value: this.props.default     //Propsとしてもらってきた値を入れる
        };
    
        this.handleChange = this.handleChange.bind(this);
      }
    
      handleChange(e){
        this.setState({value:e.target.value});
      }
    
      render(){
        const guys = this.state.guys;
        const listItems = guys.map((guy) =>
          <option key={guy.id.toString()}>
            {guy.name}
          </option>
        );
    
        // valueで動的に'selected'の要素を指定
        return <select size="3" value={this.state.value}>{listItems}</select>;
      }
    }
    
    const guys = [
      {id:10, name:"Foo"},
      {id:20, name:"Bar"},
      {id:30, name:"Hoge"}
    ];
    
    ReactDOM.render(
      <MemberList guys={guys} default="Bar" />,    //selectedの値を指定
      document.getElementById('root')
    );
    

    なおonChangeイベントを未定義だと以下のように実行時エラーが発生してしまいますのでご注意を。

    react-dom.development.js:3102 Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.

    書籍

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

    参考ページ

    reactjs.org reactjs.org