每次将新元素推入列表时,列表的 ReactJS 渲染时间都会增加

Posted

技术标签:

【中文标题】每次将新元素推入列表时,列表的 ReactJS 渲染时间都会增加【英文标题】:ReactJS render time of list increases each time new elements pushed into the list 【发布时间】:2016-04-25 00:35:49 【问题描述】:

我正在尝试使用 react js 呈现项目列表。由于我的列表项相对复杂,我创建了一个简单的小提琴来说明我的问题。

fiddle

在我的情况下,我有无限滚动的帖子,并且每次推送帖子都会很快超出合理的渲染时间,从而增加渲染时间。它从 200-250 毫秒开始,在 10 次推送后达到 500 毫秒(每次推送有 0-10 个帖子)。

如何修复新添加项目的渲染时间?或者这只是反应 js 的工作方式。

这是我的主要 ReactComponent 简化版。

React.createClass(
    shouldComponentUpdate: function (nextProps, nextState) 
        return nextState.posts.length !== this.state.posts.length;
    ,
    getInitialState: function () 
        return 
            posts: []
        ;
    ,
    // getStream is bound to a scroll event handler. Simply, this function is called when user gets close to the end of page.
    getStream: function () 
        var self = this;
        ... ajax call success: (jsonResult) ...
        self.setState(
            posts: React.addons.update(self.state.posts,  $push: jsonResult )
        );
        ... end ....
    ,
    render: function () 
        var self = this;

        var postNode = this.state.posts.map(function (post, i) 
            if (post.isActivity) 
                return (<ReactActivity key=i></ReactActivity>);
            
            else 
                return (<ReactStreamPost key=i></ReactStreamPost>);
            
        );

        return (<div>
            postNode
        </div>);
    
    );

此时 ReactStreamPost 和 ReactStreamActivity 可以是无状态组件。喜欢

var ReactStreamPost = React.createClass(
    shouldComponentUpdate: function () 
        return false;
    ,
    render: function () 
        return (
            <div className="stream-post">
                <div className="post-topbar">
                    <a href="#" className="display-name">Test USER</a>
                </div>

                <div className="post-inner-wrapper">
                    <div className="post-owner-big">
                        <div className="post-owner">
                            <img className="profile-img" />
                        </div>
                    </div>
                    <div className="post-content">
                        <div className="post-header">
                            <div className="post-owner">
                                <img className="profile-img" />
                            </div>
                            <div className="post-agt">
                                <ReactAutoGenerateTitle></ReactAutoGenerateTitle>
                            </div>
                        </div>
                        <div className="post-title">
                            <div>
                                <a href="#" className="display-name">Test USER</a><span> Post title</span>
                            </div>
                        </div>
                        <div className="attachments">
                            attachments
                        </div>

                        <div className="post-actions">
                            <div className="left-actions">
                                <a href="#">
                                    <i className="icon icon-add-info"></i><span>Add info</span>
                                </a>
                            </div>
                            <div className="right-actions">
                                <a href="#" className="toggler">
                                    <i className="icon"></i>
                                    <span>x</span>
                                </a>
                                <a href="#" className="toggler">
                                    <i className="icon"></i>
                                    <span>x</span>
                                </a>
                                <a href="#" className="share icon icon-share"></a>
                            </div>
                        </div>

                        <div className="post-comments">
                            <div className="comment-block">
                                <div className="commentor">
                                    <img />
                                </div>
                                <div className="comment">
                                    <a href="#">Test USER</a><span>: </span><span>Comment test</span>
                                </div>
                                <div className="comment-date">
                                    <span>comment date</span>
                                </div>
                            </div>

                            <div className="comment-form">
                                <div className="commentor">
                                    <img />
                                </div>
                                <div className="text-area">
                                    <textarea rows="1" className="small"></textarea>
                                </div>
                                <div className="submit-comment">
                                    <a href="#" className="icon icon-send"></a>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div className="post-date">
                    <span>post date</span>
                </div>
            </div>
        );
    
);

【问题讨论】:

你添加更多的 DOM 元素当然会增加时间。 但是每次我向 dom 添加相同数量的元素。我知道每次状态更新都会重新渲染,但这个成本对我来说似乎有点高。 请不要使用数组索引作为子元素键。如果您使用在将其他内容添加到列表时不会更改的键,您将获得更好的性能。 【参考方案1】:

这是因为每次添加更多元素时,都会重新渲染整个列表,其中还包括之前已经渲染过的列表项。

一种解决方法是将列表项放在它自己的组件中,并使用 shouldComponentUpdate 不重新渲染它。这仍然会增加时间,因为它仍然需要检查是否需要重新渲染,但增量不会那么陡峭。一个工作小提琴here。您的行组件将如下所示:-

  var Row = React.createClass(
  shouldComponentUpdate: function() 
    return false;
  ,

  render: function() 
    return ( < li > < span > some < /span><span>more</span > < span > elements < /span></li > )
  
)

【讨论】:

这是我在自己的实现中尝试做的,但要重新检查它。谢谢你的例子。 jsfiddle.net/jain208/3jj5Loky/3 - 这是两个实现的更新小提琴。 :-) 但是是的,这种方法永远不会有效,因为您仍在渲染大量项目。理想的方法是只显示列表的一个子集。

以上是关于每次将新元素推入列表时,列表的 ReactJS 渲染时间都会增加的主要内容,如果未能解决你的问题,请参考以下文章

在每次迭代期间将新值附加到列表中后,将列表值添加到字典中

Redis 基础 -- 列表 List 类型 和 List 类型的常用命令

无法将新数据合并到列表的每个数据框元素

Yii redis list列表的使用

Yii redis list列表的使用

Hibernate 将新元素保存在列表中而不删除旧元素