假设我有1000个项目的列表。我用React渲染它,就像这样:
class Parent extends React.Component {
render() {
// this.state.list is a list of 1000 items
return <List list={this.state.list} />;
}
}
class List extends React.Component {
render() {
// here we're looping through this.props.list and creating 1000 new Items
var list = this.props.list.map(item => {
return <Item key={item.key} item={item} />;
});
return <div>{list}</div>;
}
}
class Item extends React.Component {
shouldComponentUpdate() {
// here I comparing old state/props with new
}
render() {
// some rendering here...
}
}
使用相对较长的列表映射()需要大约10-20ms,我可以注意到界面中的小延迟。
每当我只需要更新一个时,我是否可以阻止重新创建1000个React对象?
答案 0 :(得分:14)
您可以使用任何状态管理库来执行此操作,以便Parent
无法跟踪this.state.list
=&gt;只有在添加新List
时,您的Item
才会重新呈现。个人Item
会在更新时重新呈现。
假设您使用redux
。
您的代码将变成这样:
// Parent.js
class Parent extends React.Component {
render() {
return <List />;
}
}
// List.js
class List extends React.Component {
render() {
var list = this.props.list.map(item => {
return <Item key={item.key} uniqueKey={item.key} />;
});
return <div>{list}</div>;
}
}
const mapStateToProps = (state) => ({
list: getList(state)
});
export default connect(mapStateToProps)(List);
// Item.js
class Item extends React.Component {
shouldComponentUpdate() {
}
render() {
}
}
const mapStateToProps = (state, ownProps) => ({
item: getItemByKey(ownProps.uniqueKey)
});
export default connect(mapStateToProps)(Item);
当然,您必须实现reducer和两个选择器getList
和getItemByKey
。
这样,如果添加了新元素,或者您更改了List
(您不应该这样做)
item.key
重新渲染
答案 1 :(得分:9)
修改强>:
我的初始建议仅解决了可能的效率改进 列表并没有解决有关限制重新呈现的问题 由于列表更改而导致的组件。
有关原始问题的简洁解决方案,请参阅@ xiaofan2406的答案。
有助于使长列表更有效和轻松的库:
答案 2 :(得分:2)
当您更改数据时,反应默认操作是渲染所有子组件,并创建虚拟dom以判断需要重新渲染哪个组件。
所以,如果我们能够让反应知道只有一个组件需要重新渲染。它可以节省时间。
您可以在列表组件中使用shouldComponentsUpdate
。
如果在这个函数中返回false,反应将不会产生虚拟的判断。
我假设你的数据是[{name: 'name_1'}, {name: 'name_2'}]
class Item extends React.Component {
// you need judge if props and state have been changed, if not
// execute return false;
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.name === this.props.name) return false;
return true;
}
render() {
return (
<li>{this.props.name}</li>
)
}
}
因为反应只是渲染已经改变的组件。因此,如果您只更改一个项目的数据,则其他人不会进行渲染。
答案 3 :(得分:1)
避免在每个渲染中循环遍历组件列表的一种方法是在渲染函数之外进行循环并将其保存到变量。
class Item extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return this.props.item != nextProps.item;
}
render() {
return <li>{this.props.item}</li>;
}
}
class List extends React.Component {
constructor(props) {
super(props);
this.items = [];
this.update = this.update.bind(this);
}
componentWillMount() {
this.props.items.forEach((item, index) => { this.items[index] = <Item key={index} item={item} /> });
}
update(index) {
this.items[index] = <Item key={index} item={'item changed'} />
this.forceUpdate();
}
render() {
return <div>
<button onClick={() => { this.update(199); }}>Update</button>
<ul>{this.items}</ul>
</div>
}
}
答案 4 :(得分:1)
您可以做一些事情:
NODE_ENV=production npm run build
或类似的。当NODE_ENV
未设置为生产时,ReactJS会执行大量安全检查,例如PropType检查。关闭这些应该可以为React渲染提供> 2倍的性能提升,并且对于您的生产构建至关重要(尽管在开发期间将其关闭 - 这些安全检查有助于防止错误!)。您可能会发现这对于您需要支持的项目数量来说已经足够了。firstRendered
/ lastRendered
道具添加到您的列表状态(首先是包含,最后独占)。在List.render
中,渲染填充空白div
(或tr
,如果适用)正确的高度(firstRendered * itemHeight
),然后渲染范围项目[firstRendered, lastRendered)
,然后另一个填充div与剩余高度((totalItems - lastRendered) * itemHeight
)。确保您给填充物和物品固定钥匙。然后你只需要在可滚动div上处理onScroll,并找出要渲染的正确范围(通常你想在顶部和底部渲染一个相当好的重叠,你也想要只触发一个setState来改变范围你接近它的边缘)。更疯狂的选择是渲染和实现自己的滚动条(我认为这是Facebook自己的FixedDataTable - https://facebook.github.io/fixed-data-table/)。这里有很多这种通用方法的例子https://react.rocks/tag/InfiniteScroll 答案 5 :(得分:-1)
您必须将一个项目与您的项目进行比较
UpdateComment=(comment)=>{
let objIndex = this.state.comments.findIndex((obj => obj.id === comment.id));
let comments = [...this.state.comments];
comments[objIndex].content = comment.content
comments[objIndex].idea = comment.idea
this.setState({comments});
}