首页 > 解决方案 > 如何避免在调度新操作时通过循环访问存储对象而重新渲染组件

问题描述

很抱歉标题令人困惑,但这个问题很难描述得这么简单。我有一个连接到 WebSocket API 并将新数据分派到 NgRx 存储的服务,如下所示:

this.priceSocket.subscribe(
          message => this.handleMessage(message),
          error => console.log(error),
          () => console.log("completed")
        )

这是我将消息传递给的 handleMessage() 函数:

handleMessage(message) {
    if (message.price) {
      this.store.dispatch({type: "PRICES", payload: {product_id: message.product_id, price: message.price}})
    }
  }

这是我非常简单的减速器:

export function priceReducer(state: object = {prices: {}}, action: Action) {
  switch (action.type) {
    case "PRICES":
      return {...state, prices: {...state.prices, [action.payload.product_id]: action.payload.price}};
  }
}

然后,在我的一个组件中,我使用.pipe(share())存储中的一部分数据,使用管道将其传递给模板| async并循环通过它为价格对象中的每个属性呈现一个子组件:

prices = this.store.select(state => state.message ? state.message.prices : null).pipe(share());

和模板:

<div class="card-wrapper">
  <app-price *ngFor="let item of prices | async | keyvalue" [priceKey]="item?.key" [priceValue]="item?.value"></app-price>
</div>

这很有效,因为它实时显示子组件中的数据,但是我发现的问题是,每次新数据来自 WebSocket 并发送到商店时,所有子组件都会重新渲染. 我认为这是因为每次调度新操作时都会重建存储,因此*NgFor再次循环整个新对象并重新渲染每个子组件。

我已经设法避免了这种行为,方法.subscribe()是在同一片数据上使用,从组件本身的数据中构建一个新对象,该对象BehaviorSubject对于我想要显示的每个实时值都有一个新对象。然后我将对象传递给模板并使用对象中的| async每个单独值而不是整个对象。这样我得到了预期的行为,它只是重新渲染相关的子组件而不是所有子组件。我想知道是否可以在不在组件中创建此对象的情况下实现相同的行为,而是将数据直接从商店传递到模板。

标签: angularrxjsngrx-store

解决方案


使用ngFor时还可以使用ngForTrackBy

定义如何跟踪可迭代项中项目更改的函数。

当在可迭代对象中添加、移动或删除项目时,指令必须重新渲染适当的 DOM 节点。为了最大限度地减少 DOM 中的流失,仅重新渲染已更改的节点

默认情况下,更改检测器假定对象实例标识了可迭代对象中的节点。当提供此函数时,指令使用调用此函数的结果来标识项目节点,而不是对象本身的标识。

该函数接收两个输入,即迭代索引和节点对象 ID。

参考:https ://angular.io/api/common/NgForOf


推荐阅读