Created
September 14, 2016 14:30
-
-
Save onionmk2/093ab2822ec7f0494a75a79fbfe8e229 to your computer and use it in GitHub Desktop.
react_tutorial_client.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const DOMPurify = require('dompurify'); | |
const React = require('react'); | |
const ReactDOM = require('react-dom'); | |
const Remarkable = require('remarkable'); | |
const Comment = React.createClass({ | |
propTypes: { | |
author: React.PropTypes.string.isRequired, | |
children: React.PropTypes.string.isRequired, | |
}, | |
render() { | |
function createMarkup(dirty) { | |
const md = new Remarkable(); | |
const parsedComment = md.render(dirty); | |
const sanitizedComment = DOMPurify.sanitize(parsedComment); | |
return { __html: sanitizedComment }; | |
} | |
return ( | |
<div className="comment"> | |
<h2 className="commentAuthor"> | |
{this.props.author} | |
</h2> | |
<span dangerouslySetInnerHTML={createMarkup(this.props.children)} /> | |
</div> | |
); | |
}, | |
}); | |
const CommentList = React.createClass({ | |
render() { | |
const commentNodes = this.props.data.map(function (item, index) { | |
return ( | |
<Comment author={item.author} key={index}> | |
{item.text} | |
</Comment> | |
); | |
}); | |
return ( | |
<div className="commentList"> | |
{commentNodes} | |
</div> | |
); | |
}, | |
}); | |
const CommentForm = React.createClass({ | |
getInitialState() { | |
return ({ | |
author: '', | |
text: '', | |
}); | |
}, | |
componentWillUnmount() { | |
if (this.xhr) { | |
this.xhr.abort(); | |
} | |
}, | |
/* | |
* renderは pureであるべきなのでsetStateしてはいけない | |
* setStateするメソッドをコールバックに「登録する」のはrenderでやってOK | |
*/ | |
handleSubmitForm(e) { | |
e.preventDefault(); | |
(function trimComments() { | |
this.setState({ | |
author: this.state.author.trim(), | |
text: this.state.text.trim(), | |
}); | |
}).bind(this); | |
(function optimisticUpdate() { | |
this.props.addCommentToState({ author: this.state.author, text: this.state.text }); | |
}).bind(this); | |
(function postComments() { | |
this.xhr = new XMLHttpRequest(); | |
this.xhr.open('POST', 'http://localhost:3000/api/comments', true); | |
this.xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); | |
const payload = JSON.stringify({ author: this.state.author, text: this.state.text }); | |
this.xhr.send(payload); | |
}).bind(this)(); | |
(function clearComments() { | |
this.setState({ author: '' }); | |
this.setState({ text: '' }); | |
}).bind(this)(); | |
}, | |
handleAuthorChange(e) { | |
this.setState({ author: e.currentTarget.value }); | |
}, | |
handleTextChange(e) { | |
this.setState({ text: e.currentTarget.value }); | |
}, | |
render() { | |
return ( | |
<form className="commentForm" onSubmit={this.handleSubmitForm}> | |
<input type="text" placeholder="YourName" value={this.state.author} onChange={this.handleAuthorChange} /> | |
<input type="text" placeholder="Say something..." value={this.state.text} onChange={this.handleTextChange} /> | |
<input type="submit" value="submit" /> | |
</form> | |
); | |
}, | |
}); | |
const CommentBox = React.createClass({ | |
addCommentsToState(newComment) { | |
const comments = this.state.data.slice(); | |
comments.push(newComment); | |
this.setState({ data: comments }); | |
}, | |
/* | |
InitialStateがないと、<CommentList data={this.state.data}/>が | |
Uncaught TypeError: Cannot read property 'data' of nullで死ぬ。 | |
*/ | |
getInitialState() { | |
return ({ data: [] }); | |
}, | |
componentDidMount() { | |
const loadCommentsFromServer = () => { | |
this.xhr = new XMLHttpRequest(); | |
this.xhr.open('GET', 'http://localhost:3000/api/comments', true); | |
this.xhr.onload = () => { | |
if (this.xhr.status === 200) { | |
const commentData = JSON.parse(this.xhr.response); | |
this.setState({ data: commentData }); | |
} else { | |
console.log(`xhr.status ${this.xhr.status}`); | |
} | |
}; | |
this.xhr.send(); | |
}; | |
/* | |
1回即呼び出ししないと、pollIntervalが長いときにAJAX前の状態がユーザーに見えてしまう。 | |
*/ | |
loadCommentsFromServer(); | |
setInterval(loadCommentsFromServer, this.props.pollInterval); | |
}, | |
componentWillUnmount() { | |
this.xhr.abort(); | |
}, | |
render() { | |
return ( | |
<div className="commentBox"> | |
<h1>Comments</h1> | |
<CommentList data={this.state.data} /> | |
<CommentForm addCommentToState={this.addCommentsToState.bind(this)} /> | |
</div> | |
); | |
}, | |
}); | |
ReactDOM.render(<CommentBox pollInterval={10000} />, document.getElementById('content')); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment