避免在 React 渲染循环中进行每次迭代绑定
Posted
技术标签:
【中文标题】避免在 React 渲染循环中进行每次迭代绑定【英文标题】:Avoiding a per-iteration bind inside React render loop 【发布时间】:2019-05-26 22:15:09 【问题描述】:我有一个渲染文件列表的 React 组件。有时列表很长,并且在这种情况下,从 UI 角度来看,分页并不理想,因此文件列表在重新渲染期间会变得很慢(例如,在拖放文件以重新排序时)。
导致缓慢的原因是,在为每个文件运行一次的循环中,有少数 bind()
调用:
render()
return (
<...>
this.props.files.map((file, index) =>
return (
<tr
key=`$index#$file.name`
onDragStart=this.onDragStart.bind(this, file, index)
onDragEnter=this.onDragEnter.bind(this, file)
onDragOver=this.onDragOver.bind(this, file)
onDragLeave=this.onDragLeave.bind(this, file)
onDrop=this.onDrop.bind(this, file, index)
/>
);
)
</...>
);
这些绑定是必要的,因此拖放处理程序知道正在拖动哪个文件以及将其放置在哪里。由于所有这些绑定为数百个文件中的每一个运行一次(即使结果元素被优化并且从未真正被渲染),我想事情有点缓慢也就不足为奇了。
我想知道是否有更好的方法来做到这一点,以某种方式将必要的每次迭代数据传递给这些函数,而不必在每次迭代中为每个函数创建唯一的绑定。
我有一个可能的解决方案,我将发布我自己的答案,但我希望得到反馈,了解这个解决方案是更好还是更差,以及它是否有任何缺点。
【问题讨论】:
【参考方案1】:所以我的解决方案是按照常规做法在构造函数中绑定函数一次,然后将每次迭代的数据放在<tr/>
DOM 元素本身上。
调用函数时,浏览器会传递一个 Event
对象,该对象包含指向附加了事件处理程序的 DOM 节点的 currentTarget
属性,从而允许再次提取每次迭代的数据。
这允许通过多次渲染反复使用相同的函数(在构造函数中只绑定一次),而无需额外绑定。
这种方法的唯一缺点是对象不能作为 DOM 属性附加,只能作为字符串附加。在我的例子中,我删除了file
对象并坚持使用数字index
,并仅在需要时使用它来查找file
对象。
constructor()
// Functions are now bound only once during construction
this.onDragStart = this.onDragStart.bind(this);
this.onDragEnter = this.onDragEnter.bind(this);
this.onDragOver = this.onDragOver.bind(this);
this.onDragLeave = this.onDragLeave.bind(this);
this.onDrop = this.onDrop.bind(this);
onDragStart(event)
// 'index' is recovered from the DOM node
const index = parseInt(event.currentTarget.dataset.index);
console.log('Event with index', index);
// Get back the 'file' object (unique to my code, but showing that
// I could not pass an object through this method and thus had to
// retrieve it again.)
const file = (index >= 0) ? this.props.files[index] : null;
// Same for other onXXX functions
// No more binds!
render()
return (
<...>
this.props.files.map((file, index) =>
return (
<tr
key=`$index#$file.name`
data-index=index
onDragStart=this.onDragStart
onDragEnter=this.onDragEnter
onDragOver=this.onDragOver
onDragLeave=this.onDragLeave
onDrop=this.onDrop
/>
);
)
</...>
);
【讨论】:
以上是关于避免在 React 渲染循环中进行每次迭代绑定的主要内容,如果未能解决你的问题,请参考以下文章
避免在 React 中由对象字面量引起的重新渲染:如何处理对象中的变量?