react.jsのお勉強


# ローカル開発環境構築

Reactはスクラッチからコードを書くのではなく、公開されているパッケージ類を組み合わせて開発環境を構築する。
パッケージはnpmコマンドでインストールする。
現在はcreate-react-appパッケージを入れれば必要なものは大方含まれている。

+ node.js インストール(npmコマンドを使うために)
https://nodejs.org/ja/download/

+ create-react-appコマンドを利用可能にするためグローバルインストール

```
c:\>npm install -g create-react-app
```

+ プロジェクトの雛形を作成

```
c:\>create-react-app my-app
```

+ ↑で作成されたpackage.jsonに記載されているパッケージをまとめてインストール  
 (node_modulesディレクトリにパッケージがインストールされる)

```
c:\>cd my-app
c:\my-app>npm install
```

+ ビルド実行＋ローカルサーバー起動

```
c:\my-app>npm start

> my-app@0.1.0 start c:\my-app
> react-scripts start
Starting the development server...
Compiled successfully!

You can now view my-app in the browser.

  Local:            http://localhost:3000/
  On Your Network:  http://192.168.56.1:3000/

Note that the development build is not optimized.
To create a production build, use npm run build.
```

↑に出てきた http://localhost:3000/ にアクセスすると雛形ページが見れる。
index.htmlやApp.jsを変更すると反映されるのが確認できる。


# ビルド

コンパイルしてアプリケーションに配備可能な状態にする。

```
c:\my-app>npm run buril
```

my-appディレクトリにbuildディレクトリが作成される。
この中にjsやらhtmlやらが生成されている。

アプリケーションに上記をアプリケーションに配置、もしくはしかるべき場所にbuildされるようにしておく。


# ワークショップ

## 準備
Ctrl+Cで一旦ローカルサーバーを止め、

+ 雛形ファイルを削除

```
c:\my-app>del src\
c:\my-app>del public\
```

+ 空ファイルを作成

```
c:\my-app>del src\index.js        #空
c:\my-app>del public\index.html
```

```index.html
<html>
<body>
Hello World!
<body>
</html>
```

npm start するとHello World!が確認できる。
この状態で index.js だったり index.html を編集していくことで開発できる。



## Hello World!を表示する

```index.html
<html>
<body>
    <div id="content">
        <!-- ここに生成されたDOMが書き出されます -->
    </div>
<body>
</html>
```

```index.js
import App from './App';
```

```App.js
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
    React.createElement(
        'p',
        { className: 'Hello' },
        'Hello World!'
    ),
    document.getElementById('content')
);
```
Hello World!でました！！！！！

入れ子のタグを作るときはこう。

```.js
ReactDOM.render(
    React.createElement(
        'p',
        { className: 'Hello' },
            React.createElement(
                'span',
                { className: 'Hello2' },
                'Hello World!'
            ),
    ),
    document.getElementById('content')
);
```

すごく見にくいので
JSX(jsの中にマークアップを書ける拡張構文)にしよう。

```.js
ReactDOM.render(
    // 指定したタグを書き出す
    // ※ 入れ子はできるけど、ルートの階層に複数タグを書くことはできない
    <p className='Hello'>
        <span className='Hello2'>Hello World!</span>
    </p>,
    document.getElementById('content')
);
```
みやすい！！！！！！ヽ（゜∀｡）ノ

## Component化

さきほどの Hello World! をコンポーネント化します。

```App.js
import React from 'react';
import ReactDOM from 'react-dom';

import Hello from './Hello';

ReactDOM.render(
    <Hello />,
    document.getElementById('content')
);
```

```Hello.js
import React, { Component } from 'react';

class Hello extends Component {
    render() { // 定義したコンポーネントをレンダリングするためのメソッド
        return(
            <p className='Hello'>
                <span className='Hello2'>Hello World!</span>
            </p>
        );
    }
}

export default Hello; // 定義したHelloをimport可能にする
```

## 値のやり取り

    props：親から渡される情報(≒引数)
    state：自Componentの状態

### Hello World! の文字列を親から渡してみる

```App.js
import React from 'react';
import ReactDOM from 'react-dom';

import Hello from './Hello';

ReactDOM.render(
    <Hello helloTitle='Hello World!' />,
    document.getElementById('content')
);
```

```Hello.js
import React, { Component } from 'react';

class Hello extends Component {
    render() {
        return(
            <p className='Hello'>
                <span className='Hello2'>{this.props.helloTitle}</span>
            </p>
        );
    }
}

export default Hello;
```

### Hello World! の文字列の状態を変更してみる

```Hello.js
import React, { Component } from 'react';

class Hello extends Component {
    constructor(props) {
        super();
        // 親から受け取ったpropsをstateの初期値とする
        this.state = { helloTitle: props.helloTitle };
    }
    changeText() {
        // 自コンポーネントのイベントにより状態を変更
        this.setState({ helloTitle: 'Hello World!!!!!!' } );
    }
    render() {
        return(
            <p className='Hello' onClick={ () => this.changeText() }>
                <span className='Hello2'>{this.state.helloTitle}</span>
            </p>
        );
    }
}

export default Hello;
```

>ここでは簡単に無名関数で onClick={ () => this.changeText() } と直接実行していますが、onClick={this.changeText} としてイベントを紐づけたほうがよいでしょう。
しかしその場合、イベント実行時にthisがバインドされない(changeText実行時にthis.setStateがundefinedになる)ので、super()後に下記を記述しておく必要があります。
```this.changeText = this.changeText.bind(this);```


## 繰り返し処理

```App.js
import React from 'react';
import ReactDOM from 'react-dom';

import Hello from './Hello';

const hellos = ['Hello', 'こんにちは', 'Здравствуйте'];

ReactDOM.render(
    <Hello hellos={hellos} />,
    document.getElementById('content')
);
```

```Hello.js
import React, { Component } from 'react';

class Hello extends Component {
    render() {
        // 配列データをmapメソッドで return JSX する
        const hellos = this.props.hellos.map((text) => {
            return <p>{text}</p>;
        });
        return(
            <p className='Hello'>
                {hellos}
            </p>
        );
    }
}

export default Hello;
```

これでもいい(定数に格納しないでjsx内に直接jsを書くことも出来る)

```.js
            <p className='Hello'>
                {this.props.hellos.map((text) => {
                    return <p>{text}</p>;
                })}
            </p>
```

## コンポーネントのライフサイクル

```Hello.js
import React, { Component } from 'react';

class Hello extends Component {
    constructor(props) {
        super();
        alert('①constructor');
        this.changeText = this.changeText.bind(this);
        // 親から受け取ったpropsをstateの初期値とする
        this.state = { helloTitle: props.helloTitle };
    }
    componentWillMount() {
        alert('②componentWillMount');
    }
    render() {
        alert('render');
        return(
            <p className='Hello' onClick={this.changeText}>
                <span className='Hello2'>{this.state.helloTitle}</span>
            </p>
        );
    }
    componentDidMount() {
        alert('③componentDidMount');
    }

    // --------------------------------

    // 廃止予定
    componentWillReceiveProps(nextProps) {
        alert('④componentWillReceiveProps');
    }

    shouldComponentUpdate(nextProps, nextState) {
        alert('⑤shouldComponentUpdate');
        // propsとstateの前後の状態を比較して再描画をコントロール
        if (this.helloTitle === nextState.helloTitle) {
            // 差分がないので無駄な再描画をしない
            return false;
        }
        return true; 
    }

    // 廃止予定
    componentWillUpdate() {
        alert('⑥componentWillUpdate');
    }

    componentDidUpdate() {
        alert('⑦componentDidUpdate');
        // 変化後のDOMを処理したい場合など
    }
    componentWillUnMount() {
        alert('⑧componentWillUnMount');
        // コンポーネントがDOMから削除される直前に実行される
        // 関連するイベントの後片付け的な処理やタイマーのキャンセルなど
    }
    changeText() {
        // 自コンポーネントのイベントにより状態を変更
        this.setState({ helloTitle: 'Hello World!!!!!!' } );
    }
}

export default Hello;
```