Javascript React:更新列表中的一项而不重新创建所有项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33080657/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
React: update one item in a list without recreating all items
提问by ch1p_
Let's say I have a list of 1000 items. And I rendering it with React, like this:
假设我有一个包含 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...
}
}
With a relatively long list map() takes about 10-20ms and I can notice a small lag in the interface.
使用相对较长的列表 map() 大约需要 10-20 毫秒,我可以注意到界面中有一点延迟。
Can I prevent recreation of 1000 React objects every time when I only need to update one?
当我只需要更新一个时,我可以防止每次重新创建 1000 个 React 对象吗?
回答by xiaofan2406
You can do it by using any state management library, so that your Parent
doesn't keep track of this.state.list
=> your List
only re-renders when new Item
is added. And the individual Item
will re-render when they are updated.
您可以使用任何状态管理库来完成此操作,这样您Parent
就不会跟踪this.state.list
=>添加List
新Item
内容时唯一的重新渲染。并且个人Item
将在更新时重新渲染。
Lets say you use redux
.
假设您使用redux
.
Your code will become something like this:
你的代码会变成这样:
// 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);
Of course, you have to implement the reducer and the two selectors getList
and getItemByKey
.
当然,你必须实现reducer和两个选择器getList
和getItemByKey
。
With this, you List
re-render will be trigger if new elements added, or if you change item.key
(which you shouldn't)
有了这个,List
如果添加了新元素,或者如果您更改item.key
(您不应该这样做),您将重新渲染
回答by Pineda
EDIT:
My inital suggestions only addressed possible efficiency improvements to rendered listsand did not address the question about limiting the re-rendering of components as a result of the list changing.
See @xiaofan2406's answer for a clean solution to the original question.
编辑:
我最初的建议只解决了渲染列表可能的效率改进问题,并没有解决由于列表更改而限制组件重新渲染的问题。
请参阅@ xiaofan2406的答案以获取原始问题的干净解决方案。
Libraries that help make rendering long lists more efficient and easy:
有助于使长列表渲染更高效和简单的库:
回答by qiuyuntao
When you change your data, react default operation is to render all children components, and creat virtual dom to judge which component is need to be rerender.
当你更改数据时,react 默认操作是渲染所有子组件,并创建虚拟 dom 来判断哪个组件需要重新渲染。
So, if we can let react know there is only one component need to be rerender. It can save times.
所以,如果我们能让 react 知道只有一个组件需要重新渲染。它可以节省时间。
You can use shouldComponentsUpdate
in your list component.
您可以shouldComponentsUpdate
在列表组件中使用。
If in this function return false, react will not create vitual dom to judge.
如果在这个函数中返回false,react不会创建虚拟dom来判断。
I assume your data like this [{name: 'name_1'}, {name: 'name_2'}]
我假设你的数据是这样的 [{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>
)
}
}
As react just render what have been changed component. So if you just change one item's data, others will not do render.
由于反应只是渲染已更改的组件。因此,如果您只更改一项的数据,则其他项将不会进行渲染。
回答by jpdelatorre
One way to avoid looping through the component list every render would be to do it outside of render function and save it to a variable.
避免在每次渲染时遍历组件列表的一种方法是在渲染函数之外执行此操作并将其保存到变量中。
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>
}
}
回答by TomW
There are a few things you can do:
您可以执行以下操作:
- When you build, make sure you are setting NODE_ENV to production. e.g.
NODE_ENV=production npm run build
or similar. ReactJS performs a lot of safety checks whenNODE_ENV
is not set to production, such as PropType checks. Switching these off should give you a >2x performance improvement for React rendering, and is vital for your production build (though leave it off during development - those safety checks help prevent bugs!). You may find this is good enough for the number of items you need to support. - If the elements are in a scrollable panel, and you can only see a few of them, you can set things up only to render the visible subset of elements. This is easiest when the items have fixed height. The basic approach is to add
firstRendered
/lastRendered
props to your List state (that's first inclusiveand last exclusiveof course). InList.render
, render a fillerblankdiv
(ortr
if applicable) of the correct height (firstRendered * itemHeight
), then your rendered range of items[firstRendered, lastRendered)
, then another filler div with the remaining height ((totalItems - lastRendered) * itemHeight
). Make sure you give your fillers and items fixed keys. You then just need to handle onScroll on the scrollable div, and work out what the correct range to render is (generally you want to render a decent overlap off the top and bottom, also you want to only trigger a setState to change the range when you get near to the edge of it). A crazier alternative is to render and implement your own scrollbar (which is what Facebook's own FixedDataTable does I think - https://facebook.github.io/fixed-data-table/). There are lots of examples of this general approach here https://react.rocks/tag/InfiniteScroll - Use a sideways loadingapproach using a state management library. For larger apps this is essential anyway. Rather than passing the Items' state down from the top, have each Item retrieve its own state, either from 'global' state (as in classical Flux), or via React context (as in modern Flux implementations, MobX, etc.). That way, when an item changes, only that item needs to re-render.
- 构建时,请确保将 NODE_ENV 设置为生产。例如
NODE_ENV=production npm run build
或类似。ReactJS 在NODE_ENV
未设置为生产时会执行很多安全检查,例如 PropType 检查。关闭这些应该可以让你的 React 渲染性能提高 2 倍以上,这对你的生产构建至关重要(尽管在开发过程中将其关闭 - 这些安全检查有助于防止错误!)。您可能会发现这对于您需要支持的项目数量来说已经足够了。 - 如果元素位于可滚动面板中,并且您只能看到其中的几个,则可以设置仅呈现元素的可见子集。当项目具有固定高度时,这是最简单的。基本方法是将
firstRendered
/lastRendered
props添加到您的 List 状态(当然,这是第一个包含的和最后一个不包含的)。在 中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 - 使用使用状态管理库的横向加载方法。对于较大的应用程序,这无论如何都是必不可少的。不是从顶部向下传递 Item 的状态,而是让每个 Item 从“全局”状态(如在经典 Flux 中)或通过 React 上下文(如在现代 Flux 实现、MobX 等中)检索自己的状态。这样,当一个项目发生变化时,只有该项目需要重新渲染。
回答by x-rw
you have to compare a item with your items
你必须将一个项目与你的项目进行比较
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});
}