如何使用changeDetectionStrategy.onPush来保持对象正确更新?

时间:2016-12-20 12:31:14

标签: angular ngrx

我在带有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根据商店状态进行更新。

我做错了什么?是关于国家的不变性还是组件内的一些问题?

1 个答案:

答案 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。)