diff --git a/client/.eslintrc b/client/.eslintrc index 881f4097..9a03ae6d 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -14,4 +14,4 @@ rules: indent: [1, 2, { SwitchCase: 1, VariableDeclarator: 2 }] react/sort-comp: 0 react/jsx-quotes: 1 - id-length: [2, {"exceptions": ["e", "i"]}] + id-length: [2, {"exceptions": ["e", "i", "_"]}] diff --git a/client/app/components/Comment.jsx b/client/app/components/Comment.jsx index e3b40d1f..a358bd80 100644 --- a/client/app/components/Comment.jsx +++ b/client/app/components/Comment.jsx @@ -1,13 +1,12 @@ import React, { PropTypes } from 'react'; import marked from 'marked'; -const Comment = React.createClass({ - displayName: 'Comment', - - propTypes: { +class Comment extends React.Component { + static displayName = 'Comment'; + static propTypes = { author: PropTypes.string.isRequired, text: PropTypes.string.isRequired, - }, + }; render() { const { author, text } = this.props; @@ -20,7 +19,7 @@ const Comment = React.createClass({ ); - }, -}); + } +} export default Comment; diff --git a/client/app/components/CommentBox.jsx b/client/app/components/CommentBox.jsx index 21e805a2..693355f4 100644 --- a/client/app/components/CommentBox.jsx +++ b/client/app/components/CommentBox.jsx @@ -2,29 +2,36 @@ import React, { PropTypes } from 'react'; import CommentForm from './CommentForm'; import CommentList from './CommentList'; +import _ from 'lodash'; -const CommentBox = React.createClass({ - displayName: 'CommentBox', +class CommentBox extends React.Component { + constructor(props, context) { + super(props, context); + this.state = {}; - propTypes: { + _.bindAll(this, '_ajaxCounter', '_isSendingAjax'); + } + + static displayName = 'CommentBox'; + static propTypes = { pollInterval: PropTypes.number.isRequired, actions: PropTypes.object.isRequired, data: PropTypes.object.isRequired, - }, + }; componentDidMount() { const { fetchComments } = this.props.actions; fetchComments(); setInterval(fetchComments, this.props.pollInterval); - }, + } - ajaxCounter() { + _ajaxCounter() { return this.props.data.get('ajaxCounter'); - }, + } - isSendingAjax() { - return this.ajaxCounter() > 0; - }, + _isSendingAjax() { + return this._ajaxCounter() > 0; + } render() { const { actions, data } = this.props; @@ -32,14 +39,14 @@ const CommentBox = React.createClass({ return (

- Comments { this.isSendingAjax() && `SENDING AJAX REQUEST! Ajax Counter is ${this.ajaxCounter()}` } + Comments { this._isSendingAjax() && `SENDING AJAX REQUEST! Ajax Counter is ${this._ajaxCounter()}` }

Text take Github Flavored Markdown. Comments older than 24 hours are deleted. Name is preserved, Text is reset, between submits.

@@ -49,7 +56,7 @@ const CommentBox = React.createClass({ />
); - }, -}); + } +} export default CommentBox; diff --git a/client/app/components/CommentForm.jsx b/client/app/components/CommentForm.jsx index cbe5c755..6cfcbeec 100644 --- a/client/app/components/CommentForm.jsx +++ b/client/app/components/CommentForm.jsx @@ -6,35 +6,37 @@ import Nav from 'react-bootstrap/lib/Nav'; import NavItem from 'react-bootstrap/lib/NavItem'; import Alert from 'react-bootstrap/lib/Alert'; import ReactCSSTransitionGroup from 'react/lib/ReactCSSTransitionGroup'; +import _ from 'lodash'; const emptyComment = { author: '', text: '' }; const textPlaceholder = 'Say something using markdown...'; -const CommentForm = React.createClass({ - displayName: 'CommentForm', +class CommentForm extends React.Component { + constructor(props, context) { + super(props, context); + this.state = { + formMode: 0, + comment: emptyComment, + }; + + _.bindAll(this, '_handleSelect', '_handleChange', '_handleSubmit', '_resetAndFocus'); + } + + static displayName = 'CommentForm'; - propTypes: { + static propTypes = { ajaxSending: PropTypes.bool.isRequired, actions: PropTypes.object.isRequired, error: PropTypes.any, - }, - - getInitialState() { - return { - formMode: 0, - comment: emptyComment, - }; - }, + }; - handleSelect(selectedKey) { + _handleSelect(selectedKey) { this.setState({ formMode: selectedKey }); - }, + } - handleChange() { + _handleChange() { let comment; - // This could also be done using ReactLink: - // http://facebook.github.io/react/docs/two-way-binding-helpers.html if (this.state.formMode < 2) { comment = { author: this.refs.author.getValue(), @@ -50,17 +52,17 @@ const CommentForm = React.createClass({ } this.setState({ comment }); - }, + } - handleSubmit(e) { + _handleSubmit(e) { e.preventDefault(); const { actions } = this.props; actions .submitComment(this.state.comment) - .then(this.resetAndFocus); - }, + .then(this._resetAndFocus); + } - resetAndFocus() { + _resetAndFocus() { // Don't reset a form that didn't submit, this results in data loss if (this.props.error) return; @@ -75,13 +77,13 @@ const CommentForm = React.createClass({ } ref.focus(); - }, + } - formHorizontal() { + _formHorizontal() { return (

-
+
@@ -117,20 +119,20 @@ const CommentForm = React.createClass({
); - }, + } - formStacked() { + _formStacked() { return (

-
+
); - }, + } - formInline() { + _formInline() { return (

- + @@ -167,7 +169,7 @@ const CommentForm = React.createClass({ placeholder="Your Name" ref="inlineAuthor" value={this.state.comment.author} - onChange={this.handleChange} + onChange={this._handleChange} disabled={this.props.ajaxSending} /> @@ -178,7 +180,7 @@ const CommentForm = React.createClass({ placeholder={textPlaceholder} ref="inlineText" value={this.state.comment.text} - onChange={this.handleChange} + onChange={this._handleChange} disabled={this.props.ajaxSending} /> @@ -195,9 +197,9 @@ const CommentForm = React.createClass({
); - }, + } - errorWarning() { + _errorWarning() { // If there is no error, there is nothing to add to the DOM if (!this.props.error) return undefined; return ( @@ -206,19 +208,19 @@ const CommentForm = React.createClass({ A server error prevented your comment from being saved. Please try again. ); - }, + } render() { let inputForm; switch (this.state.formMode) { case 0: - inputForm = this.formHorizontal(); + inputForm = this._formHorizontal(); break; case 1: - inputForm = this.formStacked(); + inputForm = this._formStacked(); break; case 2: - inputForm = this.formInline(); + inputForm = this._formInline(); break; default: throw new Error(`Unknown form mode: ${this.state.formMode}.`); @@ -230,10 +232,10 @@ const CommentForm = React.createClass({ transitionEnterTimeout={300} transitionLeaveTimeout={300} > - {this.errorWarning()} + {this._errorWarning()} -
); - }, -}); + } +} export default CommentForm; diff --git a/client/app/components/CommentList.jsx b/client/app/components/CommentList.jsx index b4321728..a6e75b4f 100644 --- a/client/app/components/CommentList.jsx +++ b/client/app/components/CommentList.jsx @@ -5,15 +5,22 @@ import ReactCSSTransitionGroup from 'react/lib/ReactCSSTransitionGroup'; import Comment from './Comment'; -const CommentList = React.createClass({ - displayName: 'CommentList', +class CommentList extends React.Component { + constructor(props, context) { + super(props, context); + this.state = {}; - propTypes: { + this._errorWarning = this._errorWarning.bind(this); + } + + static displayName = 'CommentList'; + + static propTypes = { $$comments: PropTypes.instanceOf(Immutable.List).isRequired, error: PropTypes.any, - }, + }; - errorWarning() { + _errorWarning() { // If there is no error, there is nothing to add to the DOM if (!this.props.error) return undefined; return ( @@ -22,7 +29,7 @@ const CommentList = React.createClass({ A server error prevented loading comments. Please try again. ); - }, + } render() { const { $$comments } = this.props; @@ -46,14 +53,14 @@ const CommentList = React.createClass({ transitionEnterTimeout={300} transitionLeaveTimeout={300} > - {this.errorWarning()} + {this._errorWarning()}
{commentNodes}
); - }, -}); + } +} export default CommentList; diff --git a/client/app/components/CommentScreen.jsx b/client/app/components/CommentScreen.jsx index 40439789..923f2b7d 100644 --- a/client/app/components/CommentScreen.jsx +++ b/client/app/components/CommentScreen.jsx @@ -9,13 +9,13 @@ function select(state) { return { data: state.$$commentsStore }; } -const CommentScreen = React.createClass({ - displayName: 'CommentScreen', +class CommentScreen extends React.Component { + static displayName = 'CommentScreen'; - propTypes: { + static propTypes = { dispatch: PropTypes.func.isRequired, data: PropTypes.object.isRequired, - }, + }; render() { const { dispatch, data } = this.props; @@ -42,8 +42,8 @@ const CommentScreen = React.createClass({ ); - }, -}); + } +} // Don't forget to actually use connect! export default connect(select)(CommentScreen); diff --git a/client/app/components/SimpleCommentScreen.jsx b/client/app/components/SimpleCommentScreen.jsx index 00e90233..10a19c11 100644 --- a/client/app/components/SimpleCommentScreen.jsx +++ b/client/app/components/SimpleCommentScreen.jsx @@ -4,30 +4,34 @@ import request from 'axios'; import CommentForm from './CommentForm'; import CommentList from './CommentList'; import metaTagsManager from '../utils/metaTagsManager'; +import _ from 'lodash'; -const SimpleCommentScreen = React.createClass({ - displayName: 'SimpleCommentScreen', - - getInitialState() { - return { +class SimpleCommentScreen extends React.Component { + constructor(props, context) { + super(props, context); + this.state = { $$comments: Immutable.fromJS([]), ajaxSending: false, fetchCommentsError: null, submitCommentError: null, }; - }, + + _.bindAll(this, '_fetchComments', '_handleCommentSubmit'); + } + + static displayName = 'SimpleCommentScreen'; componentDidMount() { - this.fetchComments(); - }, + this._fetchComments(); + } - fetchComments() { + _fetchComments() { return request.get('comments.json', { responseType: 'json' }) .then(res => this.setState({ $$comments: Immutable.fromJS(res.data) })) .catch(error => this.setState({ fetchCommentsError: error })); - }, + } - handleCommentSubmit(comment) { + _handleCommentSubmit(comment) { this.setState({ ajaxSending: true }); const requestConfig = { @@ -53,7 +57,7 @@ const SimpleCommentScreen = React.createClass({ ajaxSending: false, }); }); - }, + } render() { return ( @@ -65,7 +69,7 @@ const SimpleCommentScreen = React.createClass({

); - }, -}); + } +} export default SimpleCommentScreen; diff --git a/client/app/reducers/commentsReducer.js b/client/app/reducers/commentsReducer.js index 5ed231b1..239b024a 100644 --- a/client/app/reducers/commentsReducer.js +++ b/client/app/reducers/commentsReducer.js @@ -11,7 +11,7 @@ export const $$initialState = Immutable.fromJS({ submitCommentError: null, }); -export default function commentsReducer($$state = $$initialState, action) { +export default function commentsReducer($$state = $$initialState, action = null) { const { type, comment, comments, error } = action; switch (type) { diff --git a/client/app/startup/ClientApp.jsx b/client/app/startup/ClientApp.jsx index b8035b7d..9f5744cc 100644 --- a/client/app/startup/ClientApp.jsx +++ b/client/app/startup/ClientApp.jsx @@ -6,12 +6,11 @@ import CommentScreen from '../components/CommentScreen'; const App = props => { const store = createStore(props); - const reactComponent = ( - - - + return ( + + + ); - return reactComponent; }; // Export is needed for the hot reload server diff --git a/client/app/startup/ServerApp.jsx b/client/app/startup/ServerApp.jsx index 6786a0be..612bb058 100644 --- a/client/app/startup/ServerApp.jsx +++ b/client/app/startup/ServerApp.jsx @@ -6,12 +6,11 @@ import CommentScreen from '../components/CommentScreen'; const App = props => { const store = createStore(props); - const reactComponent = ( - - - + return ( + + + ); - return reactComponent; }; export default App; diff --git a/client/app/stores/commentsStore.js b/client/app/stores/commentsStore.js index 0622dce7..5d1e60ac 100644 --- a/client/app/stores/commentsStore.js +++ b/client/app/stores/commentsStore.js @@ -17,8 +17,6 @@ export default props => { const composedStore = compose( applyMiddleware(thunkMiddleware, loggerMiddleware) ); - const storeCreator = composedStore(createStore); - const store = storeCreator(reducer, initialState); - return store; + return composedStore(createStore)(reducer, initialState); };