最近Reactが結構な勢いで流行ってますよね。僕の周りではそういうプロジェクトは動いていないのでそうでもありませんが、最近のナウいプロダクトを作っているような会社では、当たり前のように使われていると思います。
僕はサーバーサイドメインなんですが、一応WEBエンジニアと名乗っているので、このビッグウェーブに乗らない手はありません。
チュートリアルを始める前に読んで欲しいこと
※公式チュートリアルをこなした時点での個人的な感想です。
まず、Reactってなんだよ!ってところですよね。概要を先に知っておくのは大事。
簡潔に言うとReactの使いドコロっていうのは、SPAでajaxバリバリで画面をゴリゴリ動かすようなプロジェクト、だと思います。基本的な流れとしては、ajaxでAPIを叩いて、ページ遷移せずにDOMをリアルタイムに書き換えていく。Jqueryでも出来るけど、管理難しいよね、じゃあReactだよね、と。Reactは一つのDOMのカタマリをコンポーネント化して管理するスタイルなので、管理しやすいよね。そういうことだと思います。
リポジトリをcloneする
早速公式のチュートリアルをやっていきます。
公式が用意してくれているのでありがたく使わせてもらいましょう。
$ npm install
フォルダ構造は以下のようになっている。僕のJSは数年前に制作会社で使っていたJqueryあたりで完全にSTOPしているので、ちょっと詳細に見てみることにします。
.
├── LICENSE
├── README.md
├── app.json
├── comments.json
├── memo.md
├── node_modules
├── package.json
├── public
│ ├── css
│ │ └── base.css
│ ├── index.html
│ └── scripts
│ └── example.js
├── requirements.txt
├── server.go
├── server.js
├── server.php
├── server.pl
├── server.py
└── server.rb
69 directories, 306 files
node_modules
フォルダは、npm install
の際に作られるフォルダで、PHP的にはVendor
フォルダのようなもの。いわゆる外部ライブラリ系がここに突っ込まれることになる。
app.json
、プロジェクト全体の設定みたいなものが格納されている。
comments.json
、表示用のダミーデータ。実際にはこういうデータをAPIから拾ってきて表示させることになると思われる。
package.json
、ライブラリ管理用のファイル。他にもいろいろ書いて置けるみたいだけど、とりあえずはライブラリ管理用と思っていて問題ないと思う。詳細は、こちらのブログが参考になった。
最終型はこれ
公式のGithubにもあると思うけど、最終型としてはこういう形になります。
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React Tutorial</title>
<script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
<script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
<script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/babel">
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
// Optimistically set an id on the new comment. It will be replaced by an
// id generated by the server. In a production application you would likely
// not use Date.now() for this and would have a more robust system in place.
comment.id = Date.now();
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: comments});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(e) {
this.setState({author: e.target.value});
},
handleTextChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.setState({author: '', text: ''});
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your name"
value={this.state.author}
onChange={this.handleAuthorChange}
/>
<input
type="text"
placeholder="Say something..."
value={this.state.text}
onChange={this.handleTextChange}
/>
<input type="submit" value="Post" />
</form>
);
}
});
var Comment = React.createClass({
rawMarkup: function() {
var md = new Remarkable();
var rawMarkup = md.render(this.props.children.toString());
return { __html: rawMarkup };
},
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</div>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" pollInterval={2000} />,
document.getElementById('content')
);
</script>
</body>
</html>
細かく見ていく
一つの部品がデカイし、HTML内に書いてて見づらいけど。
var CommentBox = React.createClass({
// ごにょごにょ
});
var CommentList = React.createClass({
// ごにょごにょ
});
var CommentForm = React.createClass({
// ごにょごにょ
});
var Comment = React.createClass({
// ごにょごにょ
});
ReactDOM.render(
<CommentBox url="/api/comments" pollInterval={2000} />,
document.getElementById('content')
);
この、var
で宣言しているのが、コンポーネントになる。なので、このチュートリアルでは4つのコンポーネントがあるね。それぞれのコンポーネントはDOM(HTMLタグ)を内包していて、それをreturn
することで、最終的にブラウザ上でHTMLをレンダリングする。レンダリングのフックは、ReactDOM.render()
で定義されている通り。
props、state
コンポーネントの中身自体は特にこれと言って特筆すべき点はないんだけど、props
、state
に関しては知っておかないといけないと思う。
丸投げして申し訳ないが、こちらの記事が参考になったので、読んでおいたほうがいいと思う。
おわりに
正直Reactの公式で用意されているチュートリアルはこれだけだ。かなり拍子抜けする。でも、正直なところ、これだけ出来ても、実戦投入には非常に高い壁があるように思う。
今後はそのあたりをちゃんと学んでいきたい。