Эффект NGRX запускает успешное действие только один раз при нескольких отправках

Я работаю над относительно простым приложением, в котором хочу узнать больше о ngrx, redux и angular 2.

Я кратко объясню настройку моего приложения, моего appstate и редукторов.

В моем приложении я хочу рисовать определенные объекты (сетки) на моем экране с помощью фреймворка webgl. Я хочу добавить сетку через хранилище ngrx, создав простые объекты со свойствами этих сеток и сохраняя их в состоянии приложения. Всякий раз, когда добавляется «сетка», я хочу использовать побочные эффекты ngrx, чтобы рисовать сетку на экране с помощью службы, которая имеет доступ к структуре webgl.

Мое (упрощенное) приложение выглядит так:

export interface AppState {
    meshList: MeshListReducer.MeshListState
}

Чтобы добавить сетку, я создал следующие функции редуктора:

case MeshActions.ADD_MESH: {
    return Object.assign({
        entities: [meshAction.payload, ...state.entities]
    });
}

case MeshActions.ADD_MESH_SUCCESS:
case MeshActions.UPDATE_MESH: {
   // This reducer function is only being called once, see below for further explanation.
   return Object.assign({}, state, {
        entities: state.entities.map(meshItem => {
            // If we can find a mesh with this id, use the sub reducer to update it.
            if (meshItem.uuid === meshAction.payload.uuid)
                 return meshSubReducer(meshItem, meshAction);

            return meshItem;
            })
        });
    }
    default: {
        return state;
    }
}

export function meshSubReducer(mesh: BaseMesh, meshAction: MeshAction): BaseMesh {
    switch (meshAction.type) {
        case MeshActions.ADD_MESH_SUCCESS:
        case MeshActions.UPDATE_MESH:
            return Object.assign({}, meshAction.payload);
        default:
            return mesh;
    }
}

И, наконец, мой класс эффектов, который содержит вызов службы фреймворка webgl и вызов действия success:

@Effect()
addMesh$ = this.actions$
    .ofType(MeshActions.ADD_MESH)
    .map((action: MeshAction) => action.payload)
    .switchMap((payload: BaseMesh) => this._worldService.addMeshToWorld(payload))
    .map(payload => this._meshActions.addMeshSuccess(payload));

И функция worldService.addMeshToWorld:

public addMeshToWorld(mesh: BaseMesh): Promise<BaseMesh> {
    let p = new Promise<BaseMesh>((resolve, reject) => {
        let renderer = this._meshRendererFactory.createMeshRenderer(mesh);
        // Every call to the ngrx effects always enters this function, and the component is sucessfully added to the canvas.
        renderer.addTo(this._world.app).then((component: WHS.Component) => {
            resolve(Object.assign({}, mesh, {
                rendererId: (<any>component).native.id
            }));
        });
    });

    return p;
}

И я вызываю все эти функции, используя следующие методы отправки:

let soccerBall = new SphereMesh({
    geometry: {
        heightSegments: 20,
        widthSegments: 20,
        radius: 5
    },
    gameMeshType: Enums.GameMeshType.Sphere,
    uuid: this._meshHelper.generateUUID(),
    meshMaterial: {
        color: 0xF2F2F2
    }
});

let soccerBall2 = new SphereMesh({
    geometry: {
        heightSegments: 20,
        widthSegments: 20,
        radius: 5
    },
    gameMeshType: Enums.GameMeshType.Sphere,
    uuid: this._meshHelper.generateUUID(),
    meshMaterial: {
        color: 0xF2F2F2
    }
});
// Dispatching these straight after each other will cause the succes action to only be dispatched once.
this._store.dispatch(this._meshActions.addMesh(soccerBall));
this._store.dispatch(this._meshActions.addMesh(soccerBall2));

Проблема, с которой я столкнулся, заключается в том, что после вызова побочного эффекта «ADD_MESH» и после добавления сетки на экран эффект вызывает только последнее действие «карта» только один раз, поэтому функция редуктора ADD_MESH_SUCCES вызывается только для одной сетки. , а не другой, см. снимок экрана:

введите здесь описание изображения

Странно то, что, когда я добавляю вторую отправку в тайм-аут, успешный вызов теперь внезапно вызывается правильно для всех отправленных элементов:

this._store.dispatch(this._meshActions.addMesh(soccerBall));
setTimeout(() => {
    // Now succes action is suddenly being called twice like it should..
    this._store.dispatch(this._meshActions.addMesh(soccerBall2));
}, 1500);

Что могло вызвать такое странное поведение? Я думал, что мое приложение было довольно простым, но мне кажется, что мне чего-то не хватает.

Я использую следующие версии пакетов (я выбрал пакеты, которые, на мой взгляд, было бы наиболее полезно упомянуть здесь:

"@angular/core": "^5.0.0",
"rxjs": "^5.5.2",
"@ngrx/effects": "^5.2.0",
"@ngrx/store": "^5.0.0",
"typescript": "~2.4.2"

person Jurgen Welschen    schedule 01.04.2018    source источник
comment
Можете ли вы попробовать заменить switchMap на concatMap в своем эффекте   -  person Okan Aslankan    schedule 01.04.2018
comment
теперь это работает! Я просмотрел документацию, но не понял, почему это решило проблему для меня. Вы знаете, почему это происходит?   -  person Jurgen Welschen    schedule 01.04.2018


Ответы (1)


Необходимо осторожно использовать switchMap внутренние эффекты, я советую не использовать их, пока не освоитесь с RxJS.

Почему switchMap опасен? Поскольку во второй раз вы отправляете одно и то же действие до завершения первого, он отключает цепочку, начатую первым, и немедленно выполняет вторую цепочку действий.

Почему concatMap работает? Он ставит действия в очередь. После того, как первая сделана, начинается вторая. Так что между действиями нет гонки.

person Okan Aslankan    schedule 01.04.2018
comment
И если порядок результирующих действий не вызовет у вас проблем в дальнейшем (когда вам не важен порядок загруженных потоков комментариев по идентификатору в качестве примера), вы можете использовать mergeMap вместо concatMap, Подробнее о мраморах mergeMap & switchMap stackoverflow.com/a/42227335/1740331 - person MTJ; 03.04.2018