如何使用 changeDetectionStrategy.onPush 保持对象正确更新?
How to use changeDetectionStrategy.onPush to keep object correctly updated?
我正在使用 ngFor 和一个用于切换任务状态的复选框在待办事项中显示任务列表。
我正在使用来自 ngrx 的商店和以下状态界面:
export interface State {
tasks: Task[];
index: { [_id: string]: Task };
};
就是当状态改变时负责更新任务的reducer:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
state.index[task._id] = Object.assign(state.index[task._id], task);
return state;
}
这里是将任务作为@Input 并将changeDetectionStrategy 设置为onPush 的任务组件:
import { Component, OnInit, Input, ChangeDetectionStrategy, DoCheck } from '@angular/core';
import {Store} from '@ngrx/store';
import { AppState } from '../../../../domain';
import { TaskActions } from '../../../../domain/actions';
@Component({
selector: 'task',
templateUrl: './task.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
styleUrls: ['./_task.component.scss']
})
export class TaskComponent implements OnInit, DoCheck {
@Input() task;
constructor(
private store: Store<AppState>,
private TaskActions: TaskActions
) { }
ngOnInit() {
}
ngDoCheck() {
console.warn('Check');
}
public toggleTaskStatus(_task) {
var new_task = Object.assign({}, _task, {['status']: (_task.status == 'todo' ? 'done' : 'todo')});
this.store.dispatch(this.TaskActions.updateTask(new_task));
}
}
使用此配置,我第一次单击复选框时商店内的任务会更新,但 UI 不会反映任何更改。相反,从第二次开始,UI 开始切换其状态,但不根据商店状态(当商店中的任务状态为 'todo' 时,UI 为 'done')。此外,要更新 UI 不会等待减速器中的成功操作(该服务正在创建一个 1 秒的轻微超时的可观察对象)。
当 changeDetectionStrategy 设置为默认值时,一切似乎都正常工作,UI 根据存储状态进行更新。
我做错了什么?是关于状态的不变性还是组件内部的一些问题?
reducer 必须 是不可变的。
这里:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
state.index[task._id] = Object.assign(state.index[task._id], task);
return state;
}
你正在改变状态。
这行代码changeDetection: ChangeDetectionStrategy.OnPush
告诉Angular如果@Input在内存中有相同的地址(引用),它应该不被更新 (以提高性能)。
在你的例子中,你改变了状态和return相同的(状态)对象。
这就是当您删除行 changeDetection: ChangeDetectionStrategy.OnPush
.
时它起作用的原因
要使其正常工作,您应该:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
// here, we return a new reference and ensure immutability
return Object.assign(
{},
state,
{
index: {
[task._id]: Object.assign(state.index[task._id], task)
}
}
)
}
为避免改变状态,您可能需要设置 ngrx-store-freeze。如果发生状态突变,它将抛出错误。
稍微谈谈 ES7
语法,Typescript
现在支持对象上的扩展运算符,我们也可能有这种语法:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
return {
{},
...state,
{
index: {
[task._id]: { ...state.index[task._id], task }
}
}
}
}
但是 Angular
目前不支持 Typescript 2.1.4
,使用此版本会破坏您的应用程序。希望它很快就会得到支持。 CF即PR。 (感谢 semver,它应该在 2017 年 3 月登陆 Angular 4.0)。
我正在使用 ngFor 和一个用于切换任务状态的复选框在待办事项中显示任务列表。 我正在使用来自 ngrx 的商店和以下状态界面:
export interface State {
tasks: Task[];
index: { [_id: string]: Task };
};
就是当状态改变时负责更新任务的reducer:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
state.index[task._id] = Object.assign(state.index[task._id], task);
return state;
}
这里是将任务作为@Input 并将changeDetectionStrategy 设置为onPush 的任务组件:
import { Component, OnInit, Input, ChangeDetectionStrategy, DoCheck } from '@angular/core';
import {Store} from '@ngrx/store';
import { AppState } from '../../../../domain';
import { TaskActions } from '../../../../domain/actions';
@Component({
selector: 'task',
templateUrl: './task.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
styleUrls: ['./_task.component.scss']
})
export class TaskComponent implements OnInit, DoCheck {
@Input() task;
constructor(
private store: Store<AppState>,
private TaskActions: TaskActions
) { }
ngOnInit() {
}
ngDoCheck() {
console.warn('Check');
}
public toggleTaskStatus(_task) {
var new_task = Object.assign({}, _task, {['status']: (_task.status == 'todo' ? 'done' : 'todo')});
this.store.dispatch(this.TaskActions.updateTask(new_task));
}
}
使用此配置,我第一次单击复选框时商店内的任务会更新,但 UI 不会反映任何更改。相反,从第二次开始,UI 开始切换其状态,但不根据商店状态(当商店中的任务状态为 'todo' 时,UI 为 'done')。此外,要更新 UI 不会等待减速器中的成功操作(该服务正在创建一个 1 秒的轻微超时的可观察对象)。
当 changeDetectionStrategy 设置为默认值时,一切似乎都正常工作,UI 根据存储状态进行更新。
我做错了什么?是关于状态的不变性还是组件内部的一些问题?
reducer 必须 是不可变的。
这里:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
state.index[task._id] = Object.assign(state.index[task._id], task);
return state;
}
你正在改变状态。
这行代码changeDetection: ChangeDetectionStrategy.OnPush
告诉Angular如果@Input在内存中有相同的地址(引用),它应该不被更新 (以提高性能)。
在你的例子中,你改变了状态和return相同的(状态)对象。
这就是当您删除行 changeDetection: ChangeDetectionStrategy.OnPush
.
要使其正常工作,您应该:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
// here, we return a new reference and ensure immutability
return Object.assign(
{},
state,
{
index: {
[task._id]: Object.assign(state.index[task._id], task)
}
}
)
}
为避免改变状态,您可能需要设置 ngrx-store-freeze。如果发生状态突变,它将抛出错误。
稍微谈谈 ES7
语法,Typescript
现在支持对象上的扩展运算符,我们也可能有这种语法:
case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
return {
{},
...state,
{
index: {
[task._id]: { ...state.index[task._id], task }
}
}
}
}
但是 Angular
目前不支持 Typescript 2.1.4
,使用此版本会破坏您的应用程序。希望它很快就会得到支持。 CF即PR。 (感谢 semver,它应该在 2017 年 3 月登陆 Angular 4.0)。