React:更新列表中的一个项目而不重新创建所有项目

时间:2015-10-12 12:03:30

标签: javascript reactjs

假设我有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对象?

6 个答案:

答案 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和两个选择器getListgetItemByKey

这样,如果添加了新元素,或者您更改了List(您不应该这样做)

,那么您将item.key重新渲染

答案 1 :(得分:9)

  

修改

     

我的初始建议仅解决了可能的效率改进   列表并没有解决有关限制重新呈现的问题   由于列表更改而导致的组件。

     

有关原始问题的简洁解决方案,请参阅@ xiaofan2406的答案。

有助于使长列表更有效和轻松的库

React Infinite

React-Virtualized

答案 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)

您可以做一些事情:

  1. 构建时,请确保将NODE_ENV设置为生产。例如NODE_ENV=production npm run build或类似的。当NODE_ENV未设置为生产时,ReactJS会执行大量安全检查,例如PropType检查。关闭这些应该可以为React渲染提供> 2倍的性能提升,并且对于您的生产构建至关重要(尽管在开发期间将其关闭 - 这些安全检查有助于防止错误!)。您可能会发现这对于您需要支持的项目数量来说已经足够了。
  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
  3. 使用状态管理库使用横向加载方法。对于较大的应用程序,这是至关重要的。不是从顶部传递Items的状态,而是让每个Item从“全局”状态(如在经典Flux中)或通过React上下文(如现代Flux实现,MobX等)中检索其自己的状态。这样,当项目更改时,只需要重新呈现该项目。

答案 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});
  }