2016-12-20 2 views
0

Я показываю список задач в todo с ngFor и флажок для переключения состояния задачи. Я использую магазин от ngrx со следующим государственным интерфейсом:Как использовать changeDetectionStrategy.onPush для корректного обновления объекта?

export interface State { 
 
    tasks: Task[]; 
 
    index: { [_id: string]: Task }; 
 
};

Это редуктор, который отвечает за обновление задачи, когда изменение статуса:

case TaskActions.UPDATE_TASK_SUCCESS: { 
 
    const task = action.payload; 
 
    state.index[task._id] = Object.assign(state.index[task._id], task); 
 

 
    return state; 
 
}

И вот задача c omponent которые принимают задачу @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)); 
 
    } 
 

 
}

При такой конфигурации, в первый раз я нажимаю на флажке задание внутри магазина обновляется, но Пользовательский интерфейс не отражает никаких изменений. Во второй раз вместо этого пользовательский интерфейс начинает переключать свой статус, но не соответствует состоянию хранилища (когда состояние задачи «todo» в хранилище, пользовательский интерфейс «сделан»). Более того, чтобы обновиться, пользовательский интерфейс не ждет действия успеха в редукторе (услуга создает наблюдаемый с небольшим перерывом в 1 секунду).

Если для параметра changeDetectionStrategy установлено значение по умолчанию, все работает корректно, с обновлением пользовательского интерфейса в соответствии с состоянием хранилища.

Что я делаю неправильно? Рассматривается ли неизменность состояния или некоторые проблемы внутри компонента?

ответ

0

Редуктор ДОЛЖЕН быть неизменным.

Здесь:

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 говорит Угловое, что если @Input имеет тот же адрес в памяти (ссылка), она должна не будет обновляться (для повышения производительности).

В вашем случае вы мутируете состояние и возвращаете тот же (государственный) объект.
Вот почему он работает, когда вы удаляете линию 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, он должен приземлиться в Angular 4.0 в марте 2017 года).

Смежные вопросы