To familiarize myself with react.js I've wrote a Facebook-like news stream including user comments. I tried make the user names changeable, so I used a two-way-binding between the users object and the feed component. But it isn't very performant and I don't know if it is a react issue or if I have a conceptual problem.
Problem Case:
Here is an simple example on JSFiddle. On top of the page you can change a user name and react updates all names.
I get the data from my own API in a format like this:
{
"feeds": [
{
"_id": "feed_1",
"user": "user_1",
"text": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam",
"comments": [
{
"_id": "comment_1",
"user": "user_2",
"content": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."
},
...
]
},
...
],
"users": [
{
"_id": "user_1",
"name": "Allan Moreno"
},
{
"_id": "user_2",
"name": "Edward Mendez"
}
...
]
}
So I have an array with feeds and an array with users. I iterate over the feeds to generate the react feed components. The feeds and comments get their users from a getUserById function I pass as react property.
var StreamContainer = React.createClass({
getUserById: function(userId) {
for(var i=0; i<this.state.users.length; i++) {
if(this.state.users[i]._id === userId) {
return this.state.users[i];
}
}
},
getInitialState: function() {
return {
"feeds": [...],
"users": [...]
};
},
render: function() {
var getUserById = this.getUserById;
return (
<div className="stream-container">
<FeedList getUserById={getUserById} feeds={this.state.feeds} />
</div>
);
}
});
var FeedList = React.createClass({
render: function() {
var getUserById = this.props.getUserById;
var feedNodes = this.props.feeds.map(function(feed) {
return [<Feed key={feed._id} feed={feed} getUserById={getUserById} />];
});
return (
<div>
<strong>Feeds:</strong>
<ol className="stream-items">
{feedNodes}
</ol>
</div>
);
}
});
var Feed = React.createClass({
render: function() {
var getUserById = this.props.getUserById;
return (
<li className="stream-item">
<strong>{getUserById(this.props.feed.user).name}:</strong> <em>{this.props.feed.text}</em>
<FeedCommentList comments={this.props.feed.comments} getUserById={getUserById} />
</li>
);
}
});
React.render(
<StreamContainer />,
document.getElementsByTagName('body')[0]
);
Now I've tried to make it a little interactive for some performance tests. I've added a list of all users to change their names dynamically. For the two-way-binding I use ReactLink .
var StreamContainer = React.createClass({
mixins: [React.addons.LinkedStateMixin],
...
render: function() {
...
var valueLink = this.linkState('users');
var handleChange = function(e) {
valueLink.requestChange(e.target.value);
};
return (
<div className="stream-container">
<UserList users={this.state.users} valueLink={valueLink} />
<FeedList getUserById={getUserById} feeds={this.state.feeds} />
</div>
);
}
});
var UserList = React.createClass({
render: function() {
var valueLink = this.props.valueLink;
var users = this.props.users.map(function(user, index) {
var thisValueLink = {};
var handleChange = function(newValue) {
valueLink.value[index].name = newValue;
valueLink.requestChange(valueLink.value);
};
thisValueLink.requestChange = handleChange;
thisValueLink.value = valueLink.value[index].name;
return [<User key={user._id} user={user} valueLink={thisValueLink} />];
});
return (
<div>
<strong>Users:</strong>
<ul>
{users}
</ul>
</div>
);
}
});
var User = React.createClass({
render: function() {
return (
<li>
<input type="text" valueLink={this.props.valueLink} /> {this.props.user.name}
</li>
);
}
});
But now I run into a performance problem. If I change the name of a user react has to check all names for updates. So if I have 100 feeds you can see the delay between input and update.
Is there a better way to pass the users to the child components? Or maybe a more performant way to handle two-way-bindings?
You could create a username dictionary object in the StreamContainer render to pass around to children that need to lookup user names:
var userNames = {};
this.state.users.forEach(function(user) {
userNames[user._id] = user.name;
});
<FeedList usernames={userNames} feeds={this.state.feeds} />
then it's just a simple key lookup, eg:
var Feed = React.createClass({
render: function() {
return (
<li className="stream-item">
<strong>{this.props.usernames[this.props.feed.user]}:</strong> <em>{this.props.feed.text}</em>
<FeedCommentList comments={this.props.feed.comments} usernames={this.props.usernames} />
</li>
);
}
});
var FeedCommentList = React.createClass({
render: function() {
var _this = this;
var commentNodes = this.props.comments.map(function(comment) {
return [
<FeedComment
key={comment._id}
username={_this.props.usernames[comment.user]}
comment={comment} />
];
});
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.