Angular повторно отображает список компонентов, когда состояние изменяется без мутации - PullRequest
0 голосов
/ 26 мая 2019

Я из React и работаю с Angular уже несколько месяцев. Компонент React обновляет только необходимый HTML при повторном рендеринге, в то время как компонент Angular, кажется, полностью рендерится, когда состояние не изменено. Если я изменяю состояние напрямую, кажется, работает нормально.

Вот мой самый простой пример:

У меня есть основной компонент , который содержит состояние:

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

и, по его мнению, выводит список элементов:

<item *ngFor="let item of items" [item]="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 Ответ

1 голос
/ 26 мая 2019

NgForOf директива, которую Angular использует для отображения списка элементов, проверяет, есть ли какие-либо изменения в массиве, который мы передаем ему.

Если вы изменяете свойство в элементах, Angular знает, что изменений нет, поскольку ссылки на объекты в массиве остаются прежними.

С другой стороны, если вы полностью замените предыдущий объект новым, то это сигнал для ngForOf переопределить этот объект.

Чтобы определить, изменяет ли объект директиву ngForOf, используйте DefaultIterableDiffer, который смотрит на функцию trackByFn, если мы ее предоставили. Если мы не предоставляем его, то функция по умолчанию выглядит следующим образом:

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

А затем Angular сравнивает результат этой функции с предыдущим значением в коллекции.

Вы можете думать о trackByFn как key в React.

Так что ваше решение может выглядеть так:

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

trackByFn(i) {
    return i;
}

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

...