とあるエンジニア系ライダーの日常

Webエンジニアの話、バイクの話

ReactでStateとPropsをちゃんと使ってみる

前回の記事でReactが使える環境は作ったので、

まずはStateとPropsをちゃんと使ってリストを作ってみる。

ListBaseにStateを持たせて、

ListにPropsとして渡して、

ListRecordを描写してみようかなと。

StateとPropsについて

State
  • そのコンポーネントが持っている状態
  • mutable data (可変のデータ)
  • maintained by component (コンポーネントによって保持)
  • should be considered private (プライベートであるべき)

this.setState({state1: "value1",state2: "value2"});

のようにコンポーネント作成後任意のタイミングで入れられる。

初期値は

this.state = {state1: "value1",state2: "value2"}

こんな感じで入れてもいい。

Props
  • コンポーネントから渡されたプロパティ
  • immutable data (不変のデータ)
  • passed in from parent (親から渡される)
  • can be defaulted & validated (デフォルト値の設定と検証が可能)

<Component props1="value1" props2="value2">

のようにコンポーネント作成時に入れられる。

作ってみた

構成は前回と同じ。

 "dependencies": {
    "react": "^15.4.2",
    "react-dom": "^15.4.2"
  },
  "devDependencies": {
    "babel-core": "^6.24.0",
    "babel-loader": "^6.4.1",
    "babel-preset-es2015": "^6.24.0",
    "babel-preset-react": "^6.23.0",
    "webpack": "^2.2.1"
  }

 こんな感じ。

最初の呼び出し元としてapp.jsxを作る。

前回はindex.html内でrenderを呼んでいたが、

app.jsx自体にやらせることにした。

app.jsx

import React from 'react';
import ReactDom from 'react-dom';
import ListBase from './listbase.jsx';

ReactDom.render(
  <ListBase/>,
  document.getElementById('listbase'));

なので、webpack.config.jsは、app.jsxをベースとしてapp.jsを作る様に

webpack.config.js

const path = require('path');

module.exports = {
    entry: {
        app: './src/app.jsx'
    },
    output: {
        path: './public',
        filename: '[name].js' // 上記entryのキーに対応したテンプレート
    },
    module: {
        loaders: [
            {
                loader: 'babel-loader',
                exclude: /node_modules/,
                test: /\.js[x]?$/,
                query: {
                    cacheDirectory: true,
                    presets: ['react', 'es2015']
                }
            }
        ]
    }
};

なので、index.htmlはapp.jsを呼び出すだけ。

index.html

<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="listbase"></div>
    <script src="public/app.js"></script>
</body>
</html>

 ここからはさくっと。

呼び出されるListBaseコンポーネントはStateデータをコンストラクターで持ち、

<List data={this.state.data}/>

として子コンポーネントに渡すだけ。

listbase.jsx

import React, {Component} from 'react'
import List from './list.jsx';

export default class ListBase extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [
        {
          id: 1,
          name: 'tanaka'
        }, {
          id: 2,
          name: 'yoshida'
        }
      ]
    };
  }
  render() {
    return (<List data={this.state.data}/>);
  }
}

渡されたdataを、

Listコンポーネント内でループしてListRecordコンポーネントをdataの数だけ生成する。

listrecord.js

import React, {Component} from 'react'
import ListRecord from './listrecord.jsx';

export default class List extends Component {
  render() {
    var rows = this.props.data.map(function(row) {
      return (<ListRecord data={row} key={row.id}/>);
    });

    return (
      <ul>
        {rows}
      </ul>
    );
  }
}

で、最終的にListRecordがPropsを描写する。

listrecord.jsx

import React, {Component} from 'react'

export default class ListRecord extends Component {
  render() {
    return (
      <li>
        {this.props.data.id}:{this.props.data.name}
      </li>
    );
  }
}

npm run webpack -pして、

index.htmlを表示してみる!

1:tanaka

2:yoshida

おお!表示された。

PropsはImmutableなので、値を変える時はListBase自体で

setStateし直すということか。

それで、

<ListRecord data={row} key={row.id}/>

keyを比較して変更のあったものを再描写してる感じかな。

(最初keyがなくて怒られた苦笑)

うん、なんとなくだけどわかってきた。

ES6で書くとfunction(){}とかなくてJSっぽくなくていい!

DebugしにくいからReactDevTool入れてちゃんとサーバー上で動かした方がいいな。

あとコンポーネントとして、

こうやってマイクロな感じで作ったコンポーネントを組み合わせて呼び出して、

全部JSじゃなくて必要なところだけコンポーネントを埋め込んでリッチにする方が、

DOM全部管理するより開発しやすいんじゃないかなーと思った。

 

ここまでやって、次はFluxとかでのデータフロー管理なわけかな。

ちょっとずつ進んでる感。