当状态无变化时,角度重新渲染组件列表

时间:2019-05-26 12:43:54

标签: javascript angular angular-state-managmement

我来自React,并且我已经在Angular工作了几个月。当重新渲染时,React组件仅更新必要的HTML,而当状态未发生变化时,Angular组件似乎完全重新渲染。如果我直接更改状态,似乎就可以了。

这是我最基本的例子:

我有主要组件,其中包含状态:

items = [{ name: "John", age: 8 }, { name: "Jane", age: 20 }];

并在其视图中呈现项目列表:

<item *ngFor="let item of items" [item]="item"></item>

项目组件如下所示:

@Component({
  selector: "item",
  template: `
    <p>
      {{ item.name }}, {{ item.age }}
      <input type="checkbox" />
    </p>
  `
})
export class ItemComponent {
  @Input() item: any;
}

我在item组件中添加了该复选框,只是为了通知该组件何时完全重新呈现。

所以我要做的是勾选复选框,并为该状态下的每个用户增加age

如果我通过改变状态来增加它,则视图将按预期更新,并且复选框保持选中状态:

this.items.forEach(item => {
  item.age++;
});

但是如果我在不直接改变状态的情况下更改age,则会重新呈现整个列表,并且不再选中该复选框:

this.items = this.items.map(item => ({
  ...item,
  age: item.age + 1
}));

这是CodeSandbox中的完整示例。

任何人都可以解释为什么会发生这种情况,并且在不改变状态时如何使列表不能完全重新呈现?

1 个答案:

答案 0 :(得分:1)

Angular用于呈现项目列表的

NgForOf指令检查我们传递给它的数组中是否有任何更改。

如果您对项目中的属性进行突变,则Angular不会更改,因为对数组中对象的引用保持不变。

另一方面,如果将以前的对象完全替换为新的对象,则ngForOf会重新呈现该对象。

要确定对象是否更改了ngForOf指令,请使用DefaultIterableDiffer,如果提供的话,它会查看trackByFn函数。如果我们不提供,则默认功能如下:

var trackByIdentity = function (index, item) { 
  return item;
};

然后该函数的Angular compares结果与集合中的先前值一样。

您可以像React中的trackByFn那样思考key

所以您的解决方案可能如下所示:

*ngFor="let item of items; trackBy: trackByFn"

trackByFn(i) {
    return i;
}

https://codesandbox.io/s/angular-67vo6