React
redux

はじめてのReact(+環境構築)

Reactとは

・UIを構築するためのJSライブラリ
・HTMLを直接は使わずJavaScriptで動的にUI生成する仕組み
・JavaScriptのエコシステム(テストツールなど)が使える
・コンポーネント指向で、独立した部品を組み合わせて作る
・Virtual DOMを用いた画面の差分描画を行い高速な画面表示を実現する

①コンポーネントとは

・UIの構成要素
・Reactでは、コンポーネントを組み合わせてUIを作る
・コンポーネントには表示系のだけでなく処理も記述する

②ステート

・コンポーネントの内部の変数のようなもの
・コンポーネントが生成される際に初期化され、その後はコンポーネントの状態の変化を保持するために使われる。
・コンポーネントの外部からはアクセスできない。
・ステートに頼らず、使わないで済むなら使わない方が良い

③プロパティ

・コンポーネントに外部から与えられるオプションのようなもの。
・HTMLで言う属性のようなもの。
・コンポーネントの内部では、プロパティはステートと同じように変数としてアクセスできる。
・コンポーネントはプロパティの値を変更してはいけない。
・プロパティは外の世界から一方的に伝えられるもので、それを使って外に何かを発信することはできない。

④JSX

・JavaScriptの中でXMLの文法によってReactコンポーネントを配置できるようにした言語。
・XMLライクのシンタックスを追加する言語拡張。
・JSXを使うとJavaScriptの中にHTMLのタグを埋め込むように記述することができる

⑤Virtual DOM


・Reactが内部で使っている仕組み
・JavaScriptで生成したDOMを模倣したデータ構造で差分計算をして、実際のDOMに反映させるため、処理が高速になる

const element = <h1 className="hello">これはJSXです</h1>;
const App = () => (
  <div>Hello React!</div>
);

const render = () => ReactDOM.render(<App />, document.getElementById('app'));
render();
//・「App」App内で定義されている<div>はHTMLの記法と似ています。これはJSXと言ってXMLであり正確にはJavaScriptに変換されるもの。
//・「render」Reactコンポーネントをブラウザに描画するためのもの

Reactのコンポーネントは再利用可能なパーツ

//コンポーネント定義
const Hello = () => (
  <div>Hello</div>
);
//コンポーネントを使う
const App = () => (
  <div>
    <Hello />
  </div>
);

・コンポーネントはHTMLのタグと区別するためコンポーネントは大文字で始まる名前にする。
・XMLはHTMLと違い必ず閉じタグが必要。 はの省略記法
・コンポーネントの定義は 使う位置より上に書くようにする

コンポーネントにスタイルをつける

・HTMLでCSSを使ってスタイルをつけるように、コンポーネントでも同じことができる。
・ReactではInline stylesという方法でスタイルをつける。

const Hello = () => (
  <div style={{ color: 'red'}}>Hello</div>
);

・外側はReact、内側はJS

const myStyle = {
  color: 'red',
  marginLeft: 100,
};

・CSSと違いキャメルケースで

props

・コンポーネントを作る目的は再利用
・再利用が有効に働くようにするにはパラメータを受け取るようにできることが重要
・Reactではコンポーネントが受け取るパラメータオブジェクトをpropsと言う
・propsは通常のJavaScriptオブジェクト

//propsを受け取る
const Hello = ({ name }) => (
  //ここの{name}はJSで、{name + '!'}ともかける
  //JSX内の{}には任意のJavaScript表現を書くことができる
  <div>Hello {name}</div>
);
//propsを渡す
const App = () => (
  <div>
    //name="World"の部分はname={"Mina" + 3}などとしてJavaScript表現を書くことも可能
    <Hello name="World" />
  </div>
);

コンポーネントを再利用する

const Hello = ({ name, color }) => (
  <div>
    //style={}の中の{ color }は{ color: color }の意味
    <p style={{ color }}>Hello {name}!</p>
  </div>
);

const App = () => (
  <div>
    <Hello name="World" color="blue" />
    <Hello name="Ebisu" color="red" />
  </div>
);

配列から展開する

・データなどの配列からコンポーネントを展開することができるとより再利用性の価値が生まれる。

//再利用するコンポーネント
const Hello = ({ name, color }) => (
  <div>
    <p style={{ color }}>Hello {name}!</p>
  </div>
);

//データの展開を利用しない場合
const App = () => (
  <div>
    <Hello name={items[0].name} color={items[0].color} />
    <Hello name={items[1].name} color={items[1].color} />
    <Hello name={items[2].name} color={items[2].color} />
    <Hello name={items[3].name} color={items[3].color} />
  </div>
);
//データの展開を利用する場合
const App = () => (
  <div>
    {items.map((item) => (
      <Hello name={item.name} color={item.color} />
    ))}
  </div>
);

フォームを利用する

//ボタンを表示する
const MyButton = () => (
  <div>
    <button onClick={() => alert('clicked')}>Click Me</button>
  </div>
);   
//テキストボックスを制御する
let textData = '';
const setTextData = (event) => {
  textData = event.target.value;
  //10文字より多く入力できないよう
  if (textData.length > 10) {
    textData = textData.slice(0, 10);
  }
  //render()をtextデータが更新されるたびに呼び出す
  render();
};
//テキストボックスを定義する
const MyBox = () => (
  <div>
    <input type="text" value={textData} onChange={setTextData} />
  </div>
);

const App = () => (
  <div>
    <MyBox />
  </div>
);

const render = () => ReactDOM.render(<App />, document.getElementById('app'));

・Reactの魅力の一つとして、フォームの値の制御が柔軟にできるということ。
いわゆるフォームのバリデーションが、任意のコードで書くことができる

フォームの使用

const items = [
  { name: 'aaa', color: 'black' },
  { name: 'bbb', color: 'green' },
  { name: 'ccc', color: 'pink' },
  { name: 'ddd', color: 'yellow' },
];
//データを入力した際の処理
let nameData = '';
const setNameData = (event) => {
  nameData = event.target.value;
  render();
};
//データ追加ボタンをタップした際の処理
const addData = () => {
  items.push({ name: nameData, color: 'black' });
  nameData = '';
  render(); 
};
//登録フォームの定義
const MyForm = () => (
  <div>
    <input type="text" value={nameData} onChange={setNameData} />
    <button onClick={addData}>Add Data</button>
  </div>
);
//表示、削除フォームの定義
const Hello2 = ({ name, color, onDelete }) => (
  <div>
    <p>
      <span style={{ color }}>Hello {name}!</span>
      <button onClick={() => onDelete()}>Delete</button>
    </p>
  </div>
);
//削除処理
const deleteItem = (index) => {
  items.splice(index, 1);
  render();
};
const App = () => (
  <div>
    <MyForm />
    {items.map((item, index) => (
      <Hello2 name={item.name} color={item.color} onDelete={() => deleteItem(index)} />
    ))}
  </div>
);

const render = () => ReactDOM.render(<App />, document.getElementById('app'));
render();

ES2015(ES6)

・ReactでコーデイングするにはES2015が便利。
・ES2015はトランスパイルが必要だけれど、JSXを利用するとトランスパイルするため敷居が下がる。

ES2015について

「const」と「let」

・ES2015ではvarは基本的に使わず、代わりにconstとletを使うことが推奨され
・constは再代入しない変数の宣言に用います。
・letは再代入が必要な変数の宣言に用います。
・再代入とimmutabilityは別物ですので注意が必要です。

const x = 1;
let y = 2;
y = 3;
object shorthand

・ES2015ではオブジェクトのプロパティ名と値の変数名が同じ場合、省略記法を使うことができます。

const foo = 'abc';
const bar = { foo }; // same as { foo: foo }
destructuring object

・destructuring assignmentという簡便な記法
・変数への代入だけでなく、関数のパラメータ宣言でも用います。

const obj = { first: 'Ebisu', last: 'JS' };
const { first, last } = obj;
// first === 'Ebisu', last === 'JS'

function printName({ first, last }) { console.log(first, last); }
printName(obj);

// same as
// function printName(name) { console.log(name.first, name.last); }
アロー関数

・() => ...という記法

const f1 = function(x) { console.log(x); };
const f2 = (x) => { console.log(x); };
// f1 and f2 is almost the same

const f3 = (x) => { return (x + 1); };
const f4 = (x) => (x + 1);
// f3 and f4 is the same

const f5 = x => x + 1;

その他

1. コンポーネントの書き方

(1)ドキュメントに記載されている基本的な書き方

createClass
var Hello = React.createClass({
  render: function() {
     return (
       <div>Hello {this.props.name}</div>
     );
  }
})

(2)ES2015からの書き方

extendsComponent
class Hello extends React.Component {
  render() {
    return (
      <div>Hello {this.props.name}</div>
    );
  }
}

(3)React v0.14から可能になったfunction components

function Hello(props) {
  return (
    <div>Hello {props.name}</div>
  );
}

(4)ES2015の場合はアロー関数でも書ける

ArrowFunction
const Hello = (props) => (
  <div>Hello {props.name}</div>
);
returnが必要な場合
const Hello = (props) => {
  // ここに何かしらの処理が入る場合
  return (
    <div>Hello {props.name}</div>
  );
};

(5)ES2015ではpropsを引数宣言で展開することができる

restructuring
const Hello = ({ name }) => (
  <div>Hello {name}</div>
);
function宣言でも可能
function Hello({ name }) {
  return (
    <div>Hello {name}</div>
  );
}

2. コンポーネントのstate

・コンポーネントにはprops以外にもstateと言う状態を持つ手段がある。

stateを使う場合
class Hello extends React.Component {
  constructor() {
    this.state = { count: 1 };
    this.incrementCount = this.incrementCount.bind(this);
  }
  incrementCount() {
    this.setState({ count: this.count + 1 });
  }
  render() {
    return (
      <div>
        Hello {this.props.name}
        <button onClick={this.incrementCount}>
          count={this.state.count}
        </button>
      </div>
    );
  }
}
react-compose-state

・個人プロジェクトなどの手軽なコーディングではsingle storeはオーバスペックである場合もあります。そのような場合でコンポーネントレベルのstateを使いつつ、上記のようなclassを使わない方法としてreact-compose-stateがあります。

const Hello = composeWithState({ count: 1 })(({ name, count, setCount }) => (
  <div>
    Hello {name}
    <button onClick={() => setCount(count + 1)}>
      count={count}
    </button>
  </div>
);

3. コンポーネントのライフサイクルメソッド

・コンポーネントにはライフサイクルメソッドというものがある。
・コンポーネントが初めて使われた時にDOMレンダリング後に呼ばれるもの。

componentDidMount
class Hello extends React.Component {
  componentDidMount() {
    console.log('Hello component is initialized.');
  }
  render() {
    return (
      <div>
        Hello {this.props.name}
      </div>
    );
  }
}

4. PropTypes

・PropTypes指定すると、propsが不足していたり、型が異なる時などに、分かりやすいエラーメッセージが出てくるため、デバッグが楽になる。

propTypesの指定例
const Hello = ({ name }) => (
  <div>Hello {name}</div>
);
Hello.propTypes = {
  name: React.PropTypes.string.isRequired,
};
defaultpropsの書き方
const Hello = ({ name } = { name: 'World' }) => (
  <div>Hello {name}</div>
);

5. コンポーネントを2種類に分類した例

・コンポーネントの役割を2種類に分ける
・「presentational component」表示に特化するコンポーネント
・「container component」データの処理を扱うコンポーネント
・分離すると、presentation componentがデータレイヤから切り離されるため、再利用性が上がるというメリットがあります。

const Hello = ({ name, onClickButton }) => (
  <div>
    Hello {name}
    <button onClick={onClickButton}>Click me</button>
  </div>
);

const HelloContainer = ({ store }) => (
  <Hello
    name={store.getState().name}
    onClickButton={store.dispatch({ type: 'CHANGE_NAME_RANDOMLY' })}
  />
);

Reduxとは

・Reduxはimmutabilityを基本としたUIの状態を管理するためのフレームワーク。

Application Stateとは

・アプリケーションの状態で、単一のデータ構造で表現するもの。
・状態を保持する変数を一箇所に集中して管理する。

let state = {
  inputValue: 0,
  resultValue: 0,
  showingResult: false,
};

Actionとは

・actionはstateを変化させる操作のっこと
・actionはtypeプロパティを持つオブジェクト
・actionには任意のプロパティを追加することができる。

const action = { type: 'PLUS' };
const action1 = { type: 'INPUT_NUMBER', number: 1 };
const action2 = { type: 'INPUT_NUMBER', number: 2 };

ActionCreaterとは

・Actionを作る関数
・必須ではない

パラメータを受け取ってActionを返す関数
const doPlus = () => ({
  type: 'PLUS',
});

const inputNumber = (number) => ({
  type: 'INPUT_NUMBER',
  number,
});

console.log(doPlus());
console.log(inputNumber(3));
console.log(inputNumber(4));
console.log(inputNumber(5));

Reducer

・stateとactionを入力とし、新しいstateを返す関数
・actionはstateを変化させる操作
・stateを変化させることができるのはreducer
・状態と状態操作を受け取り新しい状態を作る関数
例)新しい状態 = Reducer(今の状態, 状態操作);
state = reducer(state, action);

Store

・application stateを管理する場所

React環境構築

・VirtualBox centos7

(1)Node.js / npm導入

・NVMで導入する

nvm導入
#nvmをクローン
git clone git://github.com/creationix/nvm.git ~/.nvm
#.bashrcに、nvmを登録
echo . ~/.nvm/nvm.sh >> ~/.bashrc
#.bashrc再読み込み
. ~/.bashrc
#nodeのインストール
nvm install stable

(2)プロジェクトの初期化

mkdir プロジェクト名
cd プロジェクト名
npm init

=>「package.json」が作成される

(3)webpackのインストール

npm install --save webpack

(4)React / Babelのインストール

npm install --save react react-don

モジュール名 概要
react
react-dom

npm install --save babel-core babel-loader babel-preset-es2015 babel-preset-react

モジュール名 概要
babel-core
babel-loader
babel-preset-es2015
babel-preset-react

()トップページのhtmlを自動生成するPlugin

npm install --save-dev html-webpack-plugin

(5)webpackの設定ファイルを作成

・webpackを実行する際の設定ファイル

webpack.config.js
const path = require('path');

module.exports = {
    entry: {
        bundle: './src/app.js'
    },
    output: {
        path: path.join(__dirname, 'public'),
        filename: '[name].js'
    },
    module: {
        loaders: [
            {
                loader: 'babel',
                exclude: /node_modules/,
                test: /\.js[x]?$/,
                query: {
                    cacheDirectory: true,
                    presets: ['react', 'es2015']
                }
            }
        ]
    }
};
webpackコマンド 概要
context entryオプションを解決するためのベースとなる絶対パス
devtool ブラウザの開発者ツールなどでビルド前のコードを参照することができる
entry ビルドの起点となるファイルのパスを指定する
output.path 出力先のパス
output.filename 出力ファイル名
module.loaders 対象のファイルを変換するためのloaderを指定。
module.loaders.test Loaderを適用するファイルを指定する
module.loaders.loader 通すLoaderを指定する。
plugins
resolve
loaders: [
  {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    query:{
      presets: ['react', 'es2015']
    }
  }
]

node_modules ディレクトリ配下を除き、拡張子が .js のファイルすべてに対し、babel-loader という loader を適用します。
また、適用する際、loader に react と es2015 という preset を指定します。