最近几年我在React项目上度过了新的生活。
我有一个使用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更改检测?
答案 0 :(得分:3)
根据下面的模板并使用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's function body*/
}
}
答案 1 :(得分:3)
人们 answer 您,但他们没有解释您。
OnPush
策略是进行变更检测时最有效的策略。 Angular默认情况下不会实现它,因为新手常被用来查看魔术(即,当您开始使用Angular时,默认策略更易于出错且易于理解)。
要检测更改,Angular会监听您视图中的事件。使用默认策略,可能会导致很多无用的更改检测。
在推送策略中,您可以控制何时触发更改检测。
在两种情况下,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));
}
});
}
这是您可以执行的许多实现之一。我不确定这是否会按您期望的方式工作,我只是“翻译”了代码,因此您可能需要进行一两个调整。
有了它,您的视图将在集合更改时自动更新。您无需做任何事情。这符合推送策略,因为反应式编程会触发更改检测。
并且由于每个获取器都取决于您的BehaviorSubject,因此它们在该主题的每次更改时都会更新。
只是“缺点”(实际上不是),是在组件模板中必须使用异步管道:
<ng-container *ngIf="canUpload | async">...</ng-container>
如果您有任何疑问,请随时提问!
答案 2 :(得分:0)
正如Dmitry S在his answer中所写的那样,我们可以将组件的ChangeDetectionStrategy设置为ChangeDetectionStrategy.OnPush
。
这告诉Angular该组件仅取决于其@inputs()
(也称为纯),仅在以下情况下才需要检查:
Netanel Basal发布了a very helpful article on ChangeDetectionStrategy.OnPush
。
答案 3 :(得分:-1)
如果要完全控制变更检测,请使用OnPush-Strategy,获取一个changeDetectorReference
constructor(private changeDetector: ChangeDetectorRef){}
并在需要时触发更改检测,例如当数组更改或 您的上传方法。
uploadFiles() {
// ...
this.changeDetector.detectChanges()
}