Edited at

React v15.1.0 チュートリアルをReact + ES6(Babel) + Webpackで実装する

More than 3 years have passed since last update.


概要

React + ES6 + Webpackでチュートリアルを行ったのでES6バージョンのソースをメモ。

【本家】 https://facebook.github.io/react/docs/tutorial.html


ES6変換ソース一覧


webpack.config.js


/webpack.config.js


let path = require('path');
let webpack = require('webpack');

const PATHS = {
src: path.join(__dirname, 'src'),
www: path.join(__dirname, 'www')
};

module.exports = {
entry: [
`${PATHS.src}/App.jsx`
],
output: {
path: PATHS.www,
filename: 'bundle.js'
},
resolve: {
extensions: [
'',
'.js',
'.jsx'
]
},
plugins: [
new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jqeury'
})
],
devtool: 'inline-source-map',
devServer: {
contentBase: './www',
port: 3000,
hot: false,
inline: true,
colors: true
},
module: {
loaders: [
// Babel
{
test: [
/\.jsx$/,
/\.js$/
],
loaders: [
'babel'
],
exclude: /node_modules/
},
// Sass
{
test: [
/\.scss$/
],
loaders: [
'style',
'css',
'sass'
]
},
// CSS & Bootstrap
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.svg$/, loader: 'url-loader?mimetype=image/svg+xml' },
{ test: /\.woff$/, loader: 'url-loader?mimetype=application/font-woff' },
{ test: /\.woff2$/, loader: 'url-loader?mimetype=application/font-woff' },
{ test: /\.eot$/, loader: 'url-loader?mimetype=application/font-woff' },
{ test: /\.ttf$/, loader: 'url-loader?mimetype=application/font-woff' }
]
}
};



.babelrc


/.babelrc



{
"presets": [
"react",
"es2015"
]
}


App.jsx


/src/App.jsx

/*::::::::::::::::::::::::::::::::::
JS
:::::::::::::::::::::::::::::::::::*/

import React from 'react';
import ReactDOM from 'react-dom';

/*::::::::::::::::::::::::::::::::::
Components
:::::::::::::::::::::::::::::::::::*/

import CommentApp from './components/Comment/CommentApp';

/*::::::::::::::::::::::::::::::::::
InitialDOM
:::::::::::::::::::::::::::::::::::*/

let mountNode = document.getElementById('mountNode');

/*::::::::::::::::::::::::::::::::::
AppComponent Defined
:::::::::::::::::::::::::::::::::::*/

export default class App extends React.Component {
render() {
return (
<div className="container-fluid">
<CommentApp url="http://XXXXXXXXXX/dummy/comments.json" pollInterval={2000} />
</div>
);
}
}

/*::::::::::::::::::::::::::::::::::
ToDOMRendering
:::::::::::::::::::::::::::::::::::*/

ReactDOM.render(<App />, mountNode);



CommentApp.jsx


チュートリアルではCommentBox.jsxだったがAppで統一したかった為、名称変更。



/src/components/Comment/CommentApp.jsx


import React from 'react';
import CommentList from './CommentList';
import CommentForm from './CommentForm';
import $ from 'jquery';

export default class CommentApp extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{
author: 'Jack',
text: 'just setting up my twttr'
},
{
author: 'Evu',
text: 'this tweet is nice'
}
]
};
this.loadCommentsFromServer = this.loadCommentsFromServer.bind(this);
this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
}

componentDidMount() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
}

loadCommentsFromServer() {
// Ajaxは一旦コメントアウトでstateベースで実装

// $.ajax({
// url: this.props.url,
// dataType: 'json',
// cache: false,
// success: (data) => { this.setState({ data }); },
// error: (xhr, status, err) => {
// console.log(this.props.url, status, err.toString());
// }
// });

this.setState({
data: this.state.data
});
}

handleCommentSubmit(comment) {
this.setState({
data: this.state.data.concat(comment)
});
}

render() {
return (
<div className="commentApp">
<h1>CommentApp</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
}

CommentApp.propTypes = {
data: React.PropTypes.array,
url: React.PropTypes.string,
pollInterval: React.PropTypes.number
};



CommentList.jsx


/src/components/Comment/CommentList.jsx


import React from 'react';
import Comment from './Comment';

export default class CommentList extends React.Component {
render() {
let commentNodes = this.props.data.map((comment, i) => {
return (
<Comment key={i} author={comment.author}>{comment.text}</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
}

CommentList.propTypes = {
data: React.PropTypes.array
};



CommentForm.jsx


/src/components/Comment/CommentForm.jsx


import React from 'react';

export default class CommentForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleSubmit(e) {
// ブラウザ挙動停止
e.preventDefault();

// refの値を取得
let author = this.refs.author.value;
let text = this.refs.text.value;
if (!text || !author) return;

// サーバにデータを送信
this.props.onCommentSubmit({
author,
text
});

// 値を空にする
this.refs.author.value = '';
this.refs.text.value = '';
}

render() {
return (
<form
className="commentForm"
onSubmit={this.handleSubmit}
>
<input
type="text"
placeholder="YourName"
className="textArea"
ref="author"
/>
<input
type="text"
placeholder="Say Something..."
ref="text"
className="textArea"
/>
<input
type="submit"
value="Tweet"
/>
</form>
);
}
}

CommentForm.propTypes = {
onCommentSubmit: React.PropTypes.func
};



Comment.jsx


/src/components/Comment/Comment.jsx


import marked from 'marked';
import React from 'react';

export default class Comment extends React.Component {

render() {
let rawMarkup = marked(this.props.children.toString(), { sanitize: true });
return (
<div className="comment">
<h3 className="commentAuthor">
{this.props.author}
</h3>
<span dangerouslySetInnerHTML={{ __html: rawMarkup }} />
</div>
);
}
}

Comment.propTypes = {
author: React.PropTypes.string,
children: React.PropTypes.string
};



まとめ

やっぱりES6で書くと気持ちいい。

React関連の勉強をもっと進めよう。

gulpからWebpackに乗り換えたけど、設定さえ覚えればWebpackの方が便利かも。


Github

https://github.com/gemcook/kitchen.gemcook.com/tree/react-tutorial