Prepare ES6 migration

This commit is contained in:
fecaille
2016-04-06 14:47:06 +02:00
parent a335a9d213
commit 74cc6e5b3a
9 changed files with 178 additions and 169 deletions

3
.eslintrc Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "airbnb"
}

View File

@@ -2,7 +2,7 @@
"name": "springboot-react-webpack-demo", "name": "springboot-react-webpack-demo",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"bootstrap": "^3.3.6", "bootstrap": "^3.3.6",
"jquery": "^2.2.2", "jquery": "^2.2.2",
"marked": "^0.3.5", "marked": "^0.3.5",
"react": "^0.14.7", "react": "^0.14.7",
@@ -10,10 +10,15 @@
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.7.4", "babel-core": "^6.7.4",
"babel-eslint": "^6.0.2",
"babel-loader": "^6.2.4", "babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.6.0", "babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0", "babel-preset-react": "^6.5.0",
"css-loader": "^0.23.1", "css-loader": "^0.23.1",
"eslint": "^2.5.3",
"eslint-config-airbnb": "^6.2.0",
"eslint-loader": "^1.3.0",
"eslint-plugin-react": "^4.2.3",
"extract-text-webpack-plugin": "^1.0.1", "extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5", "file-loader": "^0.8.5",
"html-loader": "^0.4.3", "html-loader": "^0.4.3",
@@ -27,8 +32,9 @@
"webpack-dev-server": "^1.14.1" "webpack-dev-server": "^1.14.1"
}, },
"scripts": { "scripts": {
"watch": "NODE_ENV=development webpack-dev-server --hot --inline", "dev-build": "NODE_ENV=development webpack --d --progress --colors",
"dev-build": "NODE_ENV=development webpack --d --progress --colors", "lint": "eslint --ignore-pattern **/nashorn-*.js --ext .js,.jsx src/main/resources/static/js/**",
"prod-build": "webpack -d -p --colors" "prod-build": "webpack -d -p --colors",
"watch": "NODE_ENV=development webpack-dev-server --hot --inline"
} }
} }

View File

@@ -1,135 +0,0 @@
'use strict';
var React = require('react'),
marked = require('marked'),
$ = require('jquery');
var Comment = React.createClass({
rawMarkup: function() {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return { __html: rawMarkup };
},
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</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 CommentBox = React.createClass({
getInitialState: function() {
return {data: this.props.data || []};
},
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;
comment.id = Date.now();
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
method: 'POST',
dataType: 'json',
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)
});
},
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>
)
}
});
module.exports = {
CommentBox: CommentBox,
CommentList: CommentList,
CommentForm: CommentForm,
Comment: Comment
};

View File

@@ -1,32 +1,27 @@
"use strict";
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server'; import ReactDOMServer from 'react-dom/server';
import App from './app.jsx'; import CommentBox from './comment_box.jsx';
import $ from 'jquery';
import 'bootstrap/dist/css/bootstrap.css';
import '../css/comments.css';
import '../css/comments.less';
require('bootstrap/dist/css/bootstrap.css'); global.renderClient = function renderClient(comments) {
require('../css/comments.css'); const data = comments || [];
require('../css/comments.less'); ReactDOM.render(
<CommentBox data={data} url="/api/comments" pollInterval={2000} />,
global.renderClient = function (comments) { document.getElementById('content')
var data = comments || []; );
ReactDOM.render(
<App.CommentBox data={data} url="/api/comments" pollInterval={2000}/>,
document.getElementById('content')
);
}; };
global.renderServer = function (comments) { global.renderServer = function renderServer(comments) {
var data = Java.from(comments); const data = Java.from(comments);
return ReactDOMServer.renderToString( return ReactDOMServer.renderToString(
<App.CommentBox data={data} url="/api/comments" pollInterval={2000} /> <CommentBox data={data} url="/api/comments" pollInterval={2000} />
); );
}; };
if( !global.nashorn ) { if (!global.nashorn) {
renderClient(initialData); global.renderClient(global.initialData);
}; }

View File

@@ -0,0 +1,23 @@
import React from 'react';
import marked from 'marked';
class Comment extends React.Component {
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>
);
}
}
Comment.propTypes = {
author: React.PropTypes.string,
children: React.PropTypes.node,
};

View File

@@ -0,0 +1,65 @@
import React from 'react';
import $ from 'jquery';
import CommentForm from './comment_form.jsx';
import CommentList from './comment_list.jsx';
class CommentBox extends React.Component {
componentDidMount() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
}
getInitialStatefunction() {
return { data: this.props.data || [] };
}
loadCommentsFromServer() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function success(data) {
this.setState({ data });
}.bind(this),
error: function error(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this),
});
}
handleCommentSubmit(comment) {
const comments = this.state.data;
const copy = comment;
copy.id = Date.now();
const newComments = comments.concat([copy]);
this.setState({ data: newComments });
$.ajax({
url: this.props.url,
method: 'POST',
dataType: 'json',
data: comment,
success: function success(data) {
this.setState({ data });
}.bind(this),
error: function error(xhr, status, err) {
this.setState({ data: comments });
console.error(this.props.url, status, err.toString());
}.bind(this),
});
}
render() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
}
CommentBox.propTypes = {
data: React.PropTypes.shape({
id: React.PropTypes.number,
author: React.PropTypes.string,
text: React.PropTypes.string,
}),
pollInterval: React.PropTypes.numbe,
url: React.PropTypes.string,
};

View File

@@ -0,0 +1,47 @@
import React from 'react';
class CommentForm extends React.Component {
getInitialState() {
return { author: '', text: '' };
}
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, 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>
);
}
}
CommentForm.propTypes = {
author: React.PropTypes.string,
text: React.PropTypes.string,
onCommentSubmit: React.PropTypes.func,
};

View File

@@ -1,7 +1,5 @@
"use strict"; import 'react';
import 'react-dom';
import 'jquery';
require('react'); import 'bootstrap/dist/css/bootstrap.css';
require('react-dom');
require('jquery');
require('bootstrap/dist/css/bootstrap.css');

View File

@@ -39,6 +39,13 @@ var config = {
publicPath: 'http://localhost:' + dev_port + '/' publicPath: 'http://localhost:' + dev_port + '/'
}, },
module: { module: {
preLoaders: [
{
test: /\.jsx$|\.js$/,
loader: 'eslint-loader',
include: __dirname + '/src/main/resources/static/js'
}
],
loaders: [ loaders: [
{ {
test: /\.jsx?$/, test: /\.jsx?$/,
@@ -83,7 +90,7 @@ var config = {
return []; return [];
}, },
htmlLoader: { htmlLoader: {
removeAttributeQuotes: false, removeAttributeQuotes: false
} }
}; };