React TutorialをES2015で書いた時のメモ。
コンポーネントの構造は以下。
- CommentBox
- CommentList
- Comment
- CommentForm
準備
いろいろとnpm install
した結果のpackage.json
。
package.json
{
"scripts": {
"build": "webpack"
},
"devDependencies": {
"babel-cli": "^6.9.0",
"babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.5.0",
"webpack": "^1.13.1"
},
"dependencies": {
"marked": "^0.3.5",
"react": "^15.1.0",
"react-dom": "^15.1.0",
"superagent": "^1.8.3"
}
}
webpackでビルドするので、そちらの設定も。
webpack.config.babel.js
import path from 'path';
const src = path.resolve(__dirname, 'src');
const dist = path.resolve(__dirname, 'dist');
export default {
entry: `${src}/app.js`,
output: {
path: dist,
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.jsx?$/,
include: [src],
loader: 'babel'
}
]
}
};
ES2015 + React で記述
jsを読み込むHTMLを用意。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React Tutorial</title>
</head>
<body>
<div id="container"></div>
<script src="./dist/bundle.js"></script>
</body>
</html>
一番上のレイヤーのCommentBoxコンポーネントをrender
するjs。Webpackの設定にも書いたように、このファイルをトランスパイルすることになる。
src/app.js
import React from 'react';
import { render } from 'react-dom';
import CommentBox from './components/CommentBox';
render(
<CommentBox url="http://localhost:4567/api/comments" pollInterval={2000} />,
document.getElementById('container')
);
各コンポーネントの記述。
class記法を使ってコンポーネントを作成。
src/components/CommentBox.js
import React from 'react';
import request from 'superagent';
import CommentList from './CommentList';
import CommentForm from './CommentForm';
export default class CommentBox extends React.Component {
constructor(props) {
super(props);
this.state = {data: []};
this.loadCommentsFromServer = this.loadCommentsFromServer.bind(this);
this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
}
loadCommentsFromServer () {
request
.get(this.props.url)
.end((error, response) =>
this.setState({data: JSON.parse(response.text)}));
}
handleCommentSubmit(comment) {
const comments = this.state.data;
comment.id = Date.now();
const newComments = [...comments, comment];
this.setState({data: newComments});
request
.post(this.props.url)
.send(comment)
.set('Content-Type', 'application/x-www-form-urlencoded')
.end((error, response) => {
if (error !== null) {
this.setState({data: comments});
} else {
this.setState({data: JSON.parse(response.text)});
}
});
}
componentDidMount() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
}
render() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
}
src/components/CommentList.js
import React from 'react';
import Comment from './Comment';
export default class CommentList extends React.Component {
constructor(props) {
super(props);
}
render() {
const commentNodes = this.props.data.map((comment) =>
<Comment author={comment.author} key={comment.id}>{comment.text}</Comment>);
return (
<div className="commentList">{commentNodes}</div>
);
}
}
src/components/Comment.js
import React from 'react';
import marked from 'marked';
export default class Comment extends React.Component {
constructor(props) {
super(props);
}
rawMarkup() {
const rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return { __html: rawMarkup };
}
render() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</div>
);
}
}
src/components/CommentForm.js
import React from 'react';
export default class CommentForm extends React.Component {
constructor(props) {
super(props);
this.state = {
'author': '',
'text': ''
};
this.handleAuthorChange = this.handleAuthorChange.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleAuthorChange(e) {
this.setState({author: e.target.value});
}
handleTextChange(e) {
this.setState({text: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
const author = this.state.author.trim();
const text = this.state.text.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.setState({
'author': '',
'text': ''
});
}
render() {
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>
);
}
}
これで終わり。
npm run build
すると、トランスパイルされたdist/bundle.js
が生成されるので、それをHTMLで読み込む。
※サーバサイド
RubyのSinatraで、非常に雑な実装のやつを書いた。
Gemfile
gem 'sinatra'
gem 'sinatra-contrib'
app.rb
require 'sinatra'
require 'sinatra/reloader' if development?
require 'json'
response_body = [
{ id: 1,
author: "Peter Hunt",
text: "This is one comment"
},
{
id: 2,
author: "Jordan Walke",
text: "This is *another* comment"
}
]
get '/api/comments' do
headers 'Access-Control-Allow-Origin' => '*'
body response_body.to_json
end
post '/api/comments' do
response_body << {
id: response_body.last[:id] + 1,
author: params["author"],
text: params["text"]
}
headers 'Access-Control-Allow-Origin' => '*'
body response_body.to_json
end
ruby app.rb
を実行するとhttp://localhost:4567
で開発サーバが起動し、APIにアクセス可能になる。