рефакторинг кода из ChangeDetectionStrategy.Default в ChangeDetectionStrategy.OnPush - программирование
Подтвердить что ты не робот

рефакторинг кода из ChangeDetectionStrategy.Default в ChangeDetectionStrategy.OnPush

Я новичок в бизнесе после того, как провел последние несколько лет в проектах реагирования.

У меня есть компонент, который использует changeDetection: ChangeDetectionStrategy.OnPush и мне не нравится мое решение. Проблема в том, что мне трудно найти какие-либо хорошие примеры из реальной жизни ChangeDetectionStrategy.OnPush

Например, у меня есть немного похожий компонент:

  files: Uploads[] = [];

  get canUpload() {
    return this.files.length > 0l
  }

  get isUploading() {
    return this.files.length > 0 && this.files.some((f) => f.state === FileUpLoadState.uploading);
  }

  get activeFiles() {
    return this.files.filter((f) => f.state !== FileUpLoadState.success);
  }

  uploadFiles() {
    if (!this.files.length) {
      return;
    }

    const fileList: FileList = (event.target as HTMLInputElement).files;

    for (const uploadedFile of Array.prototype.slice.call(fileList)) {
      // do stuff
      this.files.push(new Upload(file));
    }

  }

У меня есть эти свойства, которые используются в шаблоне, как это;

 <button (click)="uploadFiles()" [disabled]="!this.canUpload">Upload</button>

Мне действительно не нравится это, использование обнаружения изменений по умолчанию не масштабируется, и когда изменения распространяются, находятся вне моего контроля.

Как я могу реорганизовать этот код, чтобы использовать обнаружение изменений OnPush?

4b9b3361

Ответ 1

Люди отвечают вам, но они не объясняют вас.

Стратегия OnPush является наиболее эффективной стратегией, когда дело доходит до обнаружения изменений. Angular не реализует его по умолчанию, потому что новички используются, чтобы увидеть магию (то есть стратегия по умолчанию более понятна и понятна, когда вы начинаете использовать Angular).

Чтобы обнаружить изменения, Angular прослушивает события в вашем представлении. При использовании стратегии по умолчанию это может привести к большому количеству бесполезного обнаружения изменений.

В стратегии push вы контролируете, когда срабатывает обнаружение изменений.

В обоих случаях Angular использует ссылки на память, чтобы знать, когда ваши данные были обновлены. Вот почему неизменность объектов так важна в Angular, а также почему реактивное программирование работает так хорошо.

При этом, если вы хотите переключиться на пуш-стратегию, вы должны использовать следующее:

  // files: Uploads[] = [];
  files: BehaviorSubject<Uploads[]> = new BehaviorSubject([]);

  add(item) {
    this.files.pipe(first()).subscribe(files => this.files.next([...files, item]));
  }

  get canUpload() {
    // return this.files.length > 0l
    return this.files.pipe(
      map(files => files.length),
      map(size => !!size)
    );
  }

  get isUploading() {
    // return this.files.length > 0 && this.files.some((f) => f.state === FileUpLoadState.uploading);
    return this.files.pipe(
      startWith(false),
      filter(files => !!files.length),
      filter(files => files.some(f => f.state === FileUpLoadState.uploading)),
      map(() => true)
    );
  }

  get activeFiles() {
    // return this.files.filter((f) => f.state !== FileUpLoadState.success);
    return this.files.pipe(
      map(files => files.filter(f => f.state !== FileUpLoadState.success)),
    );
  }

  uploadFiles() {
    /*
    if (!this.files.length) {
      return;
    }

    const fileList: FileList = (event.target as HTMLInputElement).files;

    for (const uploadedFile of Array.prototype.slice.call(fileList)) {
      // do stuff
      this.files.push(new Upload(file));
    }
    */
    this.files.pipe(
      filter(files => !!files.length),
      map(files => (event.target as HTMLInputElement).files),
      first()
    ).subscribe(files => {
      for (const uploadedFile of Array.prototype.slice.call(fileList)) {
        // do stuff
        this.add(new Upload(file));
      }
    });
  }

Это одна из многих реализаций, которые вы можете сделать. Я не уверен, что это сработает так, как вы ожидаете, я просто "перевел" код, чтобы вы могли сделать одну или две корректировки.

При этом ваше представление обновляется автоматически при изменении вашей коллекции. Вам не нужно ничего делать. Это соответствует стратегии push, потому что реактивное программирование запускает обнаружение изменений.

И поскольку каждый получатель зависит от вашего BehaviorSubject, все они обновляются при каждом изменении этого предмета.

Просто "недостатком" (который на самом деле не является) является то, что в шаблоне вашего компонента вы должны использовать асинхронный канал:

 <ng-container *ngIf="canUpload | async">...</ng-container>

если у вас есть вопросы, не стесняйтесь их задавать!

Ответ 2

В соответствии с шаблоном ниже и с использованием ChangeDetectionStrategy.OnPush, ChangeDetection запускается только после нажатия кнопки. (Реализуется только для событий DOM). Все остальное не повлияет, и DetectChanges() не запустится (например, выборка некоторых данных из какого-либо ресурса не повлияет на ваш код)

import { ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-root',
   template: '
    <button (click)="uploadFiles()" [disabled]="!this.canUpload">Upload</button>
  ',
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['./app.component.css']
})

export class AppComponent {
    uploadFiles() {
      .../*Here function body*/
    }
}

Ответ 3

Как написал Дмитрий S в своем ответе, мы можем установить для ChangeDetectionStrategy нашего компонента значение ChangeDetectionStrategy.OnPush.

Это говорит Angular, что компонент зависит только от его @inputs() (он же чистый) и должен проверяться только в следующих случаях:

  1. Ссылка на вход изменится.
  2. Событие произошло от компонента или одного из его дочерних элементов.
  3. Мы запускаем обнаружение изменений явно.

Netanel Basal опубликовал очень полезную статью на ChangeDetectionStrategy.OnPush.

Ответ 4

Если вы хотите получить полный контроль над обнаружением изменений, используя OnPush-Strategy, получите changeDetectorReference

constructor(private changeDetector: ChangeDetectorRef){}

и запускать обнаружение изменений, когда вы хотите, например, когда ваш массив изменяется или в вашем методе загрузки.

uploadFiles() { 
// ...
this.changeDetector.detectChanges()
}