Reputation: 1376
I'm using React to populate a reverse order rendering of comments, based on the official React Tutorial. Here's a JSBin of my code.
You'll notice I've embedded a Youtube video as the one existing comment. Press play on the video. Now, enter a comment. You'll notice the comment is inserted above the video, and the video is re-initialized.
If I change my code to load the posts in chronological order, the video will continue to play when a new comment is added.
Why does the video re-initialize when a comment is added in reverse chronological order? As I understand, React is supposed to intelligently remove and insert new nodes when the content should change.
Upvotes: 0
Views: 137
Reputation: 3163
Every React component instance has a "key" property, which is a unique identifier used to recognize a component. This is how React performs DOM diffing (i.e. "did this particular comment change since the last render?").
Since the code you provided does not define keys on the instance, React assigns each comment a key based on the index of the comment. When the comments are inserted in reverse chronological order, the index (and thus the key) for the video comment changes from 0 to 1, the DOM is mutated, thus reinitializing the iframe.
I've added a variable num_comments
to your code, which fixes the key issue by assigning a new comment_id every time a new comment is added.
JSBin: http://jsbin.com/wofuteyive/1/edit?js,output
The code below for completeness:
var num_comments = 0;
var Comment = React.createClass({
render: function() {
var rawMarkup = this.props.children.toString();
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentBox = React.createClass({
handleCommentSubmit: function(comment) {
var comments = this.state.data;
comments.unshift(comment);
num_comments++;
this.setState({comment_id: num_comments, data: comments});
},
getInitialState: function() {
return {data: [{
comment_id: 0,
author: 'Eric',
text: '<iframe width="560" height="315" src="https://www.youtube.com/embed/NAm1JMDZEvI" frameborder="0" allowfullscreen></iframe>'
}]};
},
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, index) {
return (
<Comment author={comment.author} key={comment.comment_id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var author = React.findDOMNode(this.refs.author).value.trim();
var text = React.findDOMNode(this.refs.text).value.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
React.findDOMNode(this.refs.author).value = '';
React.findDOMNode(this.refs.text).value = '';
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
});
React.render(
<CommentBox />,
document.getElementById('content')
);
Upvotes: 1