简体   繁体   English

关联数据和双向绑定的react.js性能不佳

[英]Bad react.js performance with associated data and two-way-binding

To familiarize myself with react.js I've wrote a Facebook-like news stream including user comments. 为了使自己熟悉react.js,我编写了一个类似Facebook的新闻流,其中包括用户评论。 I tried make the user names changeable, so I used a two-way-binding between the users object and the feed component. 我尝试使用户名可更改,因此我在users对象和feed组件之间使用了双向绑定。 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. 是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: 我从自己的API获取数据,格式如下:

{
    "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. 提要和注释通过我作为react属性传递的getUserById函数获取其用户。

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 . 对于双向绑定,我使用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. 因此,如果我有100个提要,则可以看到输入和更新之间的延迟。

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: 您可以在StreamContainer渲染中创建一个用户名字典对象,以传递给需要查找用户名的子代:

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} />
            ];
        });

...

updated jsfiddle 更新了jsfiddle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM