LoginSignup
6
7

More than 5 years have passed since last update.

【React】今更ながらReactでフロント勉強してみる【create-react-app】【サンプル】

Last updated at Posted at 2018-08-05

はじめに

フロントサイドは苦手だったんですが、自分の幅を広げる意味でも触れるようになりたいと思い今更なんですがReact勉強してみました。ただ、サンプルが載っている記事が少なかったので、今回はサンプルを中心に入門される方の手助けになるような形にしたいと思います。

「WebデベロッパーのためのReact開発入門」の一部のサンプルをES6に書き直したものを載せていこうと思います。用語も覚書き程度にまとめます。

create-react-app

reactを始めたてで環境は手っ取り早く作りたかったのでFacebook製のコマンドラインツールであるcreate-react-appで環境作りしました。

インストール

npm install -g create-react-app

アプリ作成

sample-appというアプリを作ります。以下のコマンドを実行すると必要なファイル群を自動で生成してくれます。アプリ名はキャメルケースではダメみたいです。

create-react-app sample-app

サーバー起動

以下のコマンドでlocalhost:3000でサーバー開始されます。

npm start

サンプル書いてみた

今回一部のサンプルを参考にした本はES5で書かれていたため、ES6に書き直しつつポイントを書いていきたいと思います。

複数の要素を配置するコンポーネントを利用したJSX

まず超シンプルなコンポーネントをJSX使って表現したサンプルです。

いじるファイルはindex.jsと新たなに作るmultielement.jsというファイルです。

index.htmlはcreate-react-appコマンドで作ったファイルをそのまま使います。index.jsは以下のようにします。

src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {MultiElement} from './multielement';

ReactDOM.render(
  <MultiElement />,
  document.getElementById('root')
);

multielement.jsという別ファイルからMultiElementというカスタムコンポーネントをimportしています。これによって別ファイルのコンポーネントをindex.jsに配置することができます。

ES6

カスタムコンポーネントには最低限renderを実装しておきます。

src/multielement.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class MultiElement extends React.Component {

  render() {
    <div>
      <h2>First Element</h2>
      <h2>Secound Element</h2>
      <h2>Third Element</h2>
    </div> 
  } 
}

export {MultiElement}

カスタムコンポーネントとして定義したMultiElementをindex.jsで使うためにはexport文を使います。実行すると以下のように表示されます。

スクリーンショット 2018-08-05 18.16.45.png

こんな感じでサンプルを載っけていきます。

ES5

ちなみにES5で以下にように書かれています。

src/multielement.js
var MultiElement = React.createClass({
  render: function() {
    return (
      <div>
        <h2>First Element</h2>
        <h2>Secound Element</h2>
        <h2>Third Element</h2>
      </div>
    )
  }
});

カスタムコンポーネントの定義の仕方が違いますね。

ステート付きのコンポーネントを利用したJSX①

Reactはコンポーネントの内部の状態を表現する変数的なものを「ステート」で管理するみたいです。

ユーザーからの入力をステートで管理してみます。

ES6

新たにevaluator.jsを作って以下のように書きます。index.jsに先ほどのように外部ファイルのimportをして、ReactDOM.renderの第一引数のタグを変更してください。

src/evaluator.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Evaluator extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      expression: ''
    };
  }

  reCalValue(event) {
    if (event.key === 'Enter') {
      this.setState({
        expression: event.target.value
      });
    }
  }

  render() {
    return(
      <div>
        <input
          type='text'
          onKeyPress={this.reCalValue.bind(this)}
        />
        <h2>{eval(this.state.expression)}</h2>
      </div>
    )
  }
}

export {Evaluator}

constructorではステートの初期化を行います。辞書形式で定義します。
引数にeventを受け取った関数をイベントハンドラと呼ぶらしいです。このイベントハンドラ内で呼んでいるsetStateはステートを変更する関数です。

event.target.valueでテキストエリア内に入力された文字列を受け取って、初期化したステートの一つであるexpressionを更新します。

自分がはまった部分としては、イベントハンドラ内でthisを使いたい場合はイベントハンドラを呼び出す部分で.bind(this)を追加しなければいけない点です。下記のES5ではこのような記述がないので書き直してる時にはまりました。

evalは組み込み関数?的なやつなので特に定義しなくても使えるようです。
以下が実行結果になります。

スクリーンショット 2018-08-05 19.03.39.png

Enterを押すと・・・

スクリーンショット 2018-08-05 19.04.11.png

ES5

上記で書いた.bind(this)やステートの初期化などかなり書き方に違いがでてきましたね。

src/evaluator.js
var Evaluator = React.createClass({
  getInitialState: function() {
    return {
      express: ''
    }
  },
  reCalcValue: function(e) {
    if (e.key === 'Enter') 
      this.setState ({
        expression: e.target.value
      });
  },
  render () {
    return (
      <div>
        <input
          type='text'
          onKeyPress={this.reCalValue}
        />
      </div>
    );
  }
});

スプレッド属性を利用したJSX

タグの属性をまとめて定義する仕方があるようです。
今回はindex.jsのみを変更します。

src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

var myAttributes = {
  type: 'checkbox',
  checked: 'true',
  size: 8
}

ReactDOM.render(
  <p><input {...myAttrubutes}></p>,
  document.getElementById('root')
);

htmlのようにタグに属性をゴリゴリ書くよりもスッキリまとめることができますね。{...~}という書き方には違和感がありますが(笑)
以下、実行結果です。

スクリーンショット 2018-08-05 21.05.23.png

同じような感じでstyleの定義もできます。

src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

var myStyle = {
  fontStyle: 'normal',
  fontFamily: 'Courier',
  color: 'brown'
}

ReactDOM.render(
  <h2 style={{...myStyle}}>Lets Spread!</h2>,
  document.getElementById('root')
);

スクリーンショット 2018-08-05 21.08.57.png

カスタムコンポーネントでスプレッド属性を利用したJSX

上記ではReactDOM.render内でhtml要素に対して属性を設定しました。これをカスタムコンポーネントでも使うサンプルになります。

ES6

新たにcolorbutton.jsを作ります。

src/colorbutton.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class ColorButton extends React.Component {
  render() {
    return (
      <button {...this.props}>
        {this.props.children}
      </button>
    )
  }
}

export {ColorButton}

Reactのコンポーネントが外部からデータを受け取るために利用するのが「プロパティ」と言われるものです。このプロパティをpropsという変数を通して取得できます。

なのでプロパティとして定義するのは、buttonの属性と文字列になります。

src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {ColorButton} from './colorbutton';

var myStyle = {
  color: '#A04020',
  backgroudColor: '#90C0A0',
  fontFamily: 'Serif',
  fontSize: 22
}

ReactDOM.render(
  <ColorButton
    style={{...myStyle}}
    type='button'
    disabled={false}
  >Clicked Me!</ColorButton>,
  document.getElementById('root')
)

カスタムコンポーネントでスプレット属性を利用し、子コンポーネントではプロパティにpropsを通して値を取得しました。その結果が以下になります。

スクリーンショット 2018-08-05 21.59.20.png

ステート付きコンポーネントを利用したJSX②

上記のステート付きコンポーネント①ではボタンのクリックをきっかけにステートの更新をしました。次にユーザーからの入力に対して随時状態を更新するJSXのサンプルを書いてみます。

ES6

新たにtextinput.jsを作ります。

src/textinput.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class TextInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      dispText: '入力欄'
    }
  }
    
  handleInput(event) {
    this.setState({
      dispText: event.target.value
    })
  }  

  render() {
    return (
      <div>InputText:
        <input
          type='text'
          value={this.state.dispText}
          onChange={this.handleInput.bind(this)}
        />
        <h2>{this.state.dispText}</h2>
      </div>
    )
  }
}

textの初期値として、入力欄という文字列を出力させたいため、ステートの初期化時に「入力欄」という文字列を設定しておきます。

inputタグのonChange属性は入力欄に変化があるたび呼び出されるもの定義します。今回はhandleInputというイベントハンドラを設定しました。ここでもイベントハンドラ内でthisを使用したいため.bind(this)を追加しています。

これで随時、入力欄の値をステートとして管理することができます。コンソール出力とかで確認してみるとわかりやすいです。

実行結果が以下になります。

スクリーンショット 2018-08-05 22.58.28.png

スクリーンショット 2018-08-05 23.00.31.png

こんな感じでイベントハンドラ,プロパティ,ステートなどを使いこなしてReactの書いてきました。

このあとはサンプルと出力結果のみを載せていきます。

サンプル集

複数選択できるチェックボックス

src/checkbox.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class CheckBox extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      selection: ['first']
    };
  }

  handleChange(event) {
    console.log(this.state.selection);
    var selection = this.state.selection;
    var position = selection.indexOf(event.target.value);
    if (position === -1) {
      selection.push(event.target.value);
    } else {
      selection.splice(position, 1);
    }
    this.setState({selection: selection});
    console.log(this.state);
  };

  render() {
    return(
      <div>いくつでも選んでください
      <div><input
        type='checkbox'
        value='first'
        checked={this.state.selection.indexOf('first') !== -1}
        onChange={this.handleChange.bind(this)}
      />最初の選択肢</div>
      <div><input
        type='checkbox'
        value='secound'
        checked={this.state.selection.indexOf('secound') !== -1}
        onChange={this.handleChange.bind(this)}
      />二番目の選択肢</div>
      <div><input
        type='checkbox'
        value='third'
        checked={this.state.selection.indexOf('third') !== -1}
        onChange={this.handleChange.bind(this)}
      />三番目の選択肢</div>
      </div>
    );
  }
}

export {CheckBox};

スクリーンショット 2018-08-05 23.25.47.png

スクリーンショット 2018-08-05 23.27.09.png

チェックされている状態をステートとして定義したselectionの配列で判定していますね。

ステート値による結果分岐

src/formsubmit.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class ForSubmit extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selection: this.props.selection
    }
  }

  handleChange(event) {
    var selection = this.state.selection
    var position = selection.indexOf(event.target.value)
    if (event.target.checked) {
      selection.push(event.target.value)
    } else {
      selection.splice(position, 1)
    }
    this.setState({selection: selection})
  };

  handleSubmit(event) {
    event.preventDefault();
    if(this.state.selection.length < 2) {
      return;
    }
    console.log('Submitting:', this.state.selection)
    this.setState({selection: []})
  };

  render() {
    return(<form onSubmit={this.handleSubmit.bind(this)}>つ以上を選んでください
        <div>
          <input
            type='checkbox'
            value='first'
            checked={this.state.selection.indexOf('first') !== -1}
            onChange={this.handleChange.bind(this)}
          />最初の選択        </div>
        <div>
          <input
            type='checkbox'
            value='secound'
            checked={this.state.selection.indexOf('secound') !== -1}
            onChange={this.handleChange.bind(this)}
          />次の選択        </div>
        <div>
          <input
            type='checkbox'
            value='third'
            checked={this.state.selection.indexOf('third') !== -1}
            onChange={this.handleChange.bind(this)}
          />最後の選択        </div>
        <input type='submit' value='決定' />
      </form>)
  }
}

export {ForSubmit}

スクリーンショット 2018-08-05 23.38.34.png

決定押すと、、

スクリーンショット 2018-08-05 23.37.53.png

2つ以上選択されていると出力され、2つ未満である場合は変化しない。

配列要素の自動展開

src/maptable.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class DispTable extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    var tableBody = this.props.data.map(function(person){
      return (<tbody><tr key={person.id}>
        <th>{person.name}</th>
        <th>{person.area}</th>
        <th>{person.number}</th>
      </tr></tbody>)
    })

    return (<table className='regularTable'>
      <thread>
        <tr>
          <th>名前</th>
          <th>地域</th>
          <th>番号</th>
        </tr>
      </thread>
      {tableBody}
    </table>)
  }
}

export {DispTable};
src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {DispTable} from './maptable';

var tableData = [
  {id:1, name:'山田太郎', area:'東京都港区', number:'123123'},
  {id:2, name:'山田太郎', area:'東京都港区', number:'123123'},
  {id:3, name:'山田太郎', area:'東京都港区', number:'123123'}
]

ReactDOM.render(
  <DispTable data={tableData}/>,
  document.getElementById('content')
)

スクリーンショット 2018-08-05 23.43.59.png

プロパティとして与えた配列のmapメソッドで自動展開が可能のようです。

おわりに

React始めるにあたって、基本知識であるステートやプロパティ,コンポーネントという単語が理解しづらかったのでこういったサンプルを通してReactを触り始めるの方の手助けになれば幸いです。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7