learnyoureactでReactを学ぶ
これはnpm i -g learnyoureactで学べる内容のメモ書きです。わからない時に参照すると使えるかもしれませんが、基本はご自身でコマンド叩いて勉強されると良いかと思います。
Reactとは
ReactはFacebookが開発しているJavaScriptライブラリで、MV*フレームワークでいうViewの支援を行います。
Hello World
ディレクトリの用意とpackage.jsonの作成
1 |
$ mkdir learnyoureact; cd learnyoureact; npm init -y; |
reactモジュールのインストール
1 |
npm install --save react react-dom express body-parser express-react-views@0.9.0 babel@5.8.23 |
program.jsを作ります。
1 |
touch program.js |
program.jsには以下を記述。program.jsはサーバのコードになっており、/にアクセスがあるとviews/index.jsxにアクセスするようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var express = require('express'); var app = express(); app.set('port', (process.argv[2] || 3000)); app.set('view engine', 'jsx'); app.set('views', __dirname + '/views'); app.engine('jsx', require('express-react-views').createEngine({ transformViews: false })); require('babel/register')({ ignore: false }); app.use('/', function(req, res) { res.render('index', ''); }); app.listen(app.get('port'), function() {}); |
次にviews/index.jsxを作成します。
1 2 3 4 5 6 7 8 9 |
import React from 'react'; export default class TodoBox extends React.Component{ render() { return <div className="todoBox"> Hello, world! </div> } } |
ファイルが出来たら、以下のコマンドを実行することでサーバが起動します。
1 |
node program.js |
起動できなかった場合、既にポートが使われている場合などあるのでエラー文言を元に調べましょう。
サイトにアクセスします。
1 |
http://localhost:3000/ |
Hello Worldが表示されました。初めてのReactだ!
Component
reactはcomponentという単位で部品を分割できるようです。views/index.jsxを以下のように書き換えます。HTMLの属性名がclassではなくclassNameとなっている点を注意したほうが良さそうですね。classのままの記述だと削除されてしまうようです。(JavaのJSPのstyleClassみたいな)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import React from 'react'; export default class TodoBox extends React.Component { render() { return ( <div className="todoBox"> <h1>Todos</h1> <TodoList /> <TodoForm /> </div> ); } } class TodoList extends React.Component { render() { return ( <div className="todoList"> I am a TodoList. </div> ); } } class TodoForm extends React.Component { render() { return ( <div className="todoForm"> I am a TodoForm. </div> ); } } |
TodoBoxはTodoList, TodoFormというタグを記述されていますが、各タグはその下の行からclassとして定義されています。
この状態で再度ブラウザにアクセスすると以下のようになります。
Props
親から子のコンポーネントに値を渡す方法についてです。新しくTodoクラスを作成してTodoクラスを呼び出すように変更しています。また、Todoクラスはtitle属性とテキストでプロパティを含めています。
ここで注意すべきはstyleの記法が通常のHTMLと少し異なる点でしょうか。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import React from 'react'; export default class TodoBox extends React.Component { render() { return ( <div className="todoBox"> <h1>Todos</h1> <TodoList /> <TodoForm /> </div> ); } } class TodoList extends React.Component { render() { return ( <div className="todoList"> <table style={{border: "2px solid black"}}> <tbody> <Todo title="Shopping">Milk</Todo> <Todo title="Hair cut">13:00</Todo> </tbody> </table> </div> ); } } class Todo extends React.Component { render() { return ( <tr> <td style={{border:"1px solid black"}}>{this.props.title}</td> <td style={{border:"1px solid black"}}>{this.props.children}</td> </tr> ) } } class TodoForm extends React.Component { render() { return ( <div className="todoForm"> I am a TodoForm. </div> ); } } |
PropTypes
参照する値に制約を設けます。
一番下に間違ったTodoを追記します。
1 2 3 |
<Todo title="Shopping">Milk</Todo> <Todo title="Hair cut">13:00</Todo> <Todo>15:00</Todo> |
Todoに制約を設けます。
1 2 3 |
Todo.propTypes = { title: React.PropTypes.number.isRequired }; |
実行するとWarningが表示されます。
1 2 3 4 5 6 7 8 9 10 11 |
$ learnyoureact run program.js Warning: Failed prop type: Invalid prop `title` of type `string` supplied to `Todo`, expected `number`. in Todo (created by TodoList) in TodoList (created by TodoBox) in div (created by TodoBox) in TodoBox Warning: Failed prop type: The prop `title` is marked as required in `Todo`, but its value is `undefined`. in Todo (created by TodoList) in TodoList (created by TodoBox) in div (created by TodoBox) in TodoBox |
titleの中身は必ず含まれている事と、数値であることの2つの制約を設けたのでエラーが表示されました。Todoに正しい属性を追加して、制約をstringにする変更を加えました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import React from 'react'; export default class TodoBox extends React.Component { render() { return ( <div className="todoBox"> <h1>Todos</h1> <TodoList /> <TodoForm /> </div> ); } } class TodoList extends React.Component { render() { return ( <div className="todoList"> <table style={{border: "2px solid black"}}> <tbody> <Todo title="Shopping">Milk</Todo> <Todo title="Hair cut">13:00</Todo> <Todo title="Learn React">15:00</Todo> </tbody> </table> </div> ); } } class Todo extends React.Component { render() { return ( <tr> <td style={{border:"1px solid black"}}>{this.props.title}</td> <td style={{border:"1px solid black"}}>{this.props.children}</td> </tr> ) } } Todo.propTypes = { title: React.PropTypes.string.isRequired }; class TodoForm extends React.Component { render() { return ( <div className="todoForm"> I am a TodoForm. </div> ); } } |
State
変更可能な値を定義します。state.checkedを追加し、handleChangeでstate.checkedを変更するメソッドを作成しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Todo extends React.Component { constructor(props) { super(props); this.state = {checked: true}; } handleChange(e) { this.setState({checked: e.target.checked}); } render() { return ( <tr> <td style={{border: "1px solid black"}}> <input type="checkbox" checked={this.state.checked} onChange={this.handleChange}/> </td> <td style={{border: "1px solid black"}}>{this.props.title}</td> <td style={{border: "1px solid black"}}>{this.props.children}</td> </tr> ) } } |
クリックするとチェックボックスが切り替わりました。(ただしstateの変更を確認できない。。8問目ISOMORPHICで実はstateは機能していないことが説明されてました。)
CSS
共通のCSS部分をstyle変数にまとめます。
style={{border:”1px solid black”}} で設定していましたが、styleオブジェクトを定義することでstyle={tableContent.style} と書けるようになっていますね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
import React from 'react'; export default class TodoBox extends React.Component { render() { return ( <div className="todoBox"> <h1>Todos</h1> <TodoList /> <TodoForm /> </div> ); } } class TodoList extends React.Component { render() { return ( <div className="todoList"> <table style={{border: "2px solid black"}}> <tbody> <Todo title="Shopping">Milk</Todo> <Todo title="Hair cut">13:00</Todo> <Todo title="Learn React">15:00</Todo> </tbody> </table> </div> ); } } class Todo extends React.Component { constructor(props) { super(props); this.state = {checked: false}; } handleChange(e) { this.setState({checked: e.target.checked}); } render() { return ( <tr> <td style={style.tableContent}> <input type="checkbox" checked={this.state.checked} onChange={this.handleChange}/> </td> <td style={style.tableContent}>{this.props.title}</td> <td style={style.tableContent}>{this.props.children}</td> </tr> ); } } Todo.propTypes = { title: React.PropTypes.string.isRequired }; class TodoForm extends React.Component { render() { return ( <div className="todoForm"> I am a TodoForm. </div> ); } } let style = { tableContent: { border: "1px solid black" } }; |
Props_From_Server
サーバからComponentに値を渡します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
export default class TodoBox extends React.Component { render() { return ( <div className="todoBox"> <h1>Todos</h1> <TodoList data = {this.props.data} /> <TodoForm /> </div> ); } } class TodoList extends React.Component { render() { var todo = this.props.data.map(function(obj) { return <Todo title={obj.title} key={obj.title}>{obj.detail}</Todo>}); return ( <div className = "todoList"> <table style={{border: "2px solid black"}}> <tbody> {todo} </tbody> </table> </div> ); } } |
次に、サーバサイドのレスポンスにtodoListの値を入れます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var express = require('express'); var app = express(); app.set('port', (process.argv[2] || 3001)); app.set('view engine', 'jsx'); app.set('views', __dirname + '/views'); app.engine('jsx', require('express-react-views').createEngine({transformViews: false})); require('babel/register')({ ignore: false }); var data = [ {title: 'Shopping', detail: process.argv[3]}, {title: 'Hair cut', detail: process.argv[4]} ]; app.use('/', function (req, res) { res.render('index', {data: data}); }); app.listen(app.get('port'), function () { }); |
サーバサイドのレスポンスに従って値が変更するようになりました。
続きます。
その他
Reactドキュメント
https://facebook.github.io/react/docs/getting-started-ja-JP.html
JSXシンタックス
https://facebook.github.io/react/docs/jsx-in-depth-ja-JP.html