我在带有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已完成')。此外,要获得更新,UI不会等待reducer中的成功操作(该服务正在创建一个稍微超时1秒的可观察对象)。
当changeDetectionStrategy设置为默认值时,一切看起来都正常,UI根据商店状态进行更新。
我做错了什么?是关于国家的不变性还是组件内的一些问题?
答案 0 :(得分:0)
reducer 必须是不可变的。
这里:
ln -s layoutname.html.eco php-layoutname.php.eco
你正在改变状态。
这行代码layout
告诉Angular如果@Input在内存中具有相同的地址(引用),则不会更新(以提高性能)。
在你的情况下,你改变状态并返回相同的(状态)对象
这就是为什么当你删除行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
为避免改变状态,您可能需要设置ngrx-store-freeze。如果发生状态突变,它将引发错误。
只是说一点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)
}
}
)
}
现在支持对象上的spread运算符,我们也可能有这样的语法:
ES7
但Typescript
暂时不支持case TaskActions.UPDATE_TASK_SUCCESS: {
const task = action.payload;
return {
{},
...state,
{
index: {
[task._id]: { ...state.index[task._id], task }
}
}
}
}
,使用此版本会破坏您的应用。它很快就会得到支持。 CF PR。 (感谢semver,它应该在2017年3月登陆Angular 4.0。)