Closed guillaumemaka closed 2 years ago
HI, i have an issue when I dispatch action, the action is dispatch twice.
Here how I declare actions and effects
actions.ts
import { actionsFactory, props } from '@ngneat/effects'; import { Downtime } from 'src/app/core/domain/models/downtime'; import { DowntimesFilter } from './downtimes.state'; import { CreateDowntimeDTO } from './models/create-downtime.dto'; import { DeleteDowntineDTO } from './models/delete-downtine-dto'; import { EditDowntimeDTO } from './models/edit-downtime.dto'; import { UpdateDowntineDTO } from './models/update-downtine-dto'; const actions = actionsFactory('downtimes'); export const setDowntimes = actions.create( 'SET_DOWNTIMES', props<{ downtimes: Array<Downtime> }>() ); export const filterDowntimes = actions.create( 'FILTER_DOWNTIMES', props<{ filter: DowntimesFilter }>() ); export const setLoading = actions.create( 'SET_LOADING', props<{ loading: boolean }>() ); export const editDowntime = actions.create( 'EDIT_DOWNTIME', props<{ downtime: EditDowntimeDTO }>() ); export const updateDowntime = actions.create( 'UPDATE_DOWNTIME', props<{ downtime: UpdateDowntineDTO }>() ); export const createDowntime = actions.create( 'CREATE_DOWNTIME', props<{ downtime: CreateDowntimeDTO }>() ); export const newDowntime = actions.create('NEW_DOWNTIME'); export const deleteDowntime = actions.create( 'DELETE_DOWNTIME', props<{ downtime: DeleteDowntineDTO }>() ); export const loadDowntimes = actions.create( 'LOAD_DOWNTIMES', props<{ client: string; plant: string; startTimestamp: number; endTimestamp: number; nodeId: string; }>() ); export const loadWorkUnitsForDowntime = actions.create( 'LOAD_WORKUNIT_FOR_DOWNTIME', props<{ client: string; plant: string; nodeId: string; }>() );
effects.ts
import { Injectable } from '@angular/core'; import { createEffect, ofType } from '@ngneat/effects'; import { map, switchMap, tap } from 'rxjs'; import { Downtime } from 'src/app/core/domain/models/downtime'; import { ApplicationErrorCode, applicationErrors } from '../../errors'; import { ConsoleLogger } from '../../logger/console/console-logger.service'; import { configure, debug } from '../../rxjs/operators/debug'; import { ODataApiService } from '../../services/odata-api/odata-api.service'; import { GetMachineBreakdownsBetweenTimePeriodOutput } from '../../services/odata-api/outputs/get-machine-breakdowns-between-time-period-output'; import { GetNodeAndImmediateChildrenOutput } from '../../services/odata-api/outputs/get-node-and-immediate-children-output'; import * as messagesActs from '../messages/messages.actions'; import { ERROR, NOTIFICATION, SUCCESS } from '../messages/types'; import * as acts from './downtimes.actions'; import { DowntimeRepository } from './downtimes.repository'; import { CreateDowntimeDTO } from './models/create-downtime.dto'; import { DeleteDowntineDTO } from './models/delete-downtine-dto'; import { NewDowntimeDTO } from './models/new-downtime.dto'; import { NodeDetails, NodeType } from './models/node-details'; import { UpdateDowntineDTO } from './models/update-downtine-dto'; @Injectable({ providedIn: 'root', }) export class DowntimesEffects { constructor( private downtimeRepo: DowntimeRepository, private oDataService: ODataApiService, private logger: ConsoleLogger ) { configure(this.logger.level, this.logger.logWithDate); } loadDowntimes$ = createEffect((actions) => actions.pipe( ofType(acts.loadDowntimes), switchMap((payload) => this.oDataService.getMachineBreakdownsBetweenTimePeriod({ client: payload.client, plant: payload.plant, nodeId: payload.nodeId, startTimestamp: payload.startTimestamp.toString(), endTimestamp: payload.endTimestamp.toString(), plantTimezoneOffset: 0, }) ), debug('DowntimesEffects::loadDowntimes'), map((res) => { this.downtimeRepo.setDowntimes(toDowntimes(res)); }) ) ); loadWorkUnitsForDowntime$ = createEffect( (actions) => actions.pipe( ofType(acts.loadWorkUnitsForDowntime), // if I put a debug() here it log 4 times switchMap((payload) => this.oDataService.getNodeAndImmediateChildren({ client: payload.client, plant: payload.plant, nodeId: payload.nodeId, }) ), // Log output x2 debug('DowntimesEffects::loadWorkUnitsForDowntime'), tap((res) => { this.downtimeRepo.setWorkUnitForDowntime(toNodeDetails(res)); }) ), { dispatch: false } // <---- true doesn't change anything ); newDowntime$ = createEffect( (actions) => actions.pipe( ofType(acts.newDowntime), debug('DowntimesEffects::newDowntime'), map(() => this.downtimeRepo.setDowntimeDTO(new NewDowntimeDTO())) ), { dispatch: false } ); createDowntime$ = createEffect( (actions) => actions.pipe( ofType(acts.createDowntime), debug('DowntimesEffects::createDowntime'), switchMap((payload: { downtime: CreateDowntimeDTO }) => this.oDataService.reportMultipleDowntime({ client: payload.downtime.client, plant: payload.downtime.plant, nodeID: payload.downtime.nodeID, downStartEndList: [payload.downtime], }) ), map((res) => { return res.d.outputCode === 0 ? messagesActs.showMessage({ category: SUCCESS, messageOrError: 'Downtime added successfuly!', showAs: NOTIFICATION, }) : messagesActs.showMessage({ category: ERROR, messageOrError: applicationErrors[ApplicationErrorCode.CannotCreateDowntime], showAs: NOTIFICATION, }); }) ), { dispatch: true } ); editDowntime$ = createEffect((actions) => actions.pipe( ofType(acts.editDowntime), debug('DowntimesEffects::editDowntime'), tap((payload) => this.downtimeRepo.setDowntimeDTO(payload.downtime)) ) ); updateDowntime$ = createEffect( (actions) => actions.pipe( ofType(acts.updateDowntime), debug('DowntimesEffects::updateDowntime'), switchMap((payload: { downtime: UpdateDowntineDTO }) => this.oDataService.updateDowntime(payload.downtime) ), map((res) => { return res.d.outputCode === 0 ? messagesActs.showMessage({ category: SUCCESS, messageOrError: 'Downtime updated successfuly!', showAs: NOTIFICATION, }) : messagesActs.showMessage({ category: ERROR, messageOrError: applicationErrors[ApplicationErrorCode.CannotUpdateDowntime], showAs: NOTIFICATION, }); }) ), { dispatch: true } ); deleteDowntime$ = createEffect( (actions) => actions.pipe( ofType(acts.deleteDowntime), debug('DowntimesEffects::deleteDowntime'), switchMap((payload: { downtime: DeleteDowntineDTO }) => this.oDataService.deleteDowntime(payload.downtime) ), map((res) => { return res.d.outputCode === 0 ? messagesActs.showMessage({ category: SUCCESS, messageOrError: 'Downtime deleted successfuly!', showAs: NOTIFICATION, }) : messagesActs.showMessage({ category: ERROR, messageOrError: applicationErrors[ApplicationErrorCode.CannotDeleteDowntime], showAs: NOTIFICATION, }); }) ), { dispatch: true } ); }
import { Injectable, InjectionToken } from '@angular/core'; import { createStore, distinctUntilArrayItemChanged, propsFactory, } from '@ngneat/elf'; import { selectAllEntities, setEntities, withEntities, } from '@ngneat/elf-entities'; import { localStorageStrategy, persistState } from '@ngneat/elf-persist-state'; import { distinct, distinctUntilChanged } from 'rxjs'; import { Downtime } from 'src/app/core/domain/models/downtime'; import { DowntimesFilter } from './downtimes.state'; import { NodeDetails } from './models/node-details'; import { DowntimeDTOS } from './types'; const { withFilter, selectFilter, setFilter } = propsFactory('filter', { initialValue: DowntimesFilter.ALL_DOWNTIMES, }); const { withLoading, setLoading, selectLoading } = propsFactory('loading', { initialValue: false, }); const { withDowntimeDto, setDowntimeDto, selectDowntimeDto, resetDowntimeDto } = propsFactory('downtimeDto', { initialValue: {} as DowntimeDTOS, }); const { withWorkUnitForDowntime, setWorkUnitForDowntime, selectWorkUnitForDowntime, } = propsFactory('workUnitForDowntime', { initialValue: [] as NodeDetails[], }); const store = createStore( { name: 'downtimes', }, withEntities<Downtime, 'downId'>({ idKey: 'downId' }), withWorkUnitForDowntime(), withFilter(DowntimesFilter.ALL_DOWNTIMES), withLoading(false), withDowntimeDto(undefined) ); export const persist = persistState(store, { key: 'downtimes', storage: localStorageStrategy, }); export const DOWNTIMES_REPOSITORY = new InjectionToken<DowntimeRepository>( 'DOWNTIMES_REPOSITORY' ); @Injectable({ providedIn: 'root' }) export class DowntimeRepository { filter$ = store.pipe(selectFilter()).pipe(distinctUntilChanged()); downtimes$ = store.pipe(selectAllEntities()); loading$ = store.pipe(selectLoading()).pipe(distinctUntilChanged()); workUnitsForDowntime$ = store .pipe(selectWorkUnitForDowntime()) .pipe(distinctUntilArrayItemChanged()); downtimeDto$ = store.pipe(selectDowntimeDto()); constructor() {} setDowntimes(downtimes: Array<Downtime>) { store.update(setEntities(downtimes)); } setDowntimeDTO(downtime: DowntimeDTOS) { store.update(setDowntimeDto(downtime)); } setWorkUnitForDowntime(workunits: NodeDetails[]) { store.update(setWorkUnitForDowntime(workunits)); } setLoading(loading: boolean) { store.update(setLoading(loading)); } reset() { store.update(resetDowntimeDto()); } }
core.module.ts
@NgModule({ declarations: [], imports: [ CommonModule, HttpClientModule, SharedModule, EffectsNgModule.forFeature([ DowntimesEffects, ]), ] }) export class CoreModule {}
@NgModule({ declarations: [], imports: [ CommonModule, HttpClientModule, SharedModule, EffectsNgModule.forRoot([]), ] }) export class AppModule {}
override ngOnInit(): void { this.downtimeRepository.downtimeDto$ .pipe(takeUntil(this.onDestroy$)) .subscribe({ next: (downtime) => { // OPen a dialog (x2) this.addOrEditDowntimeService.createOrEditDowntime(downtime); }, }); }
I can't reproduce this behaviour with a clean slate project.
If someone have hint let me know.
PS:
OS: Mac OS 12 NPM packages:
"@ngneat/effects-ng": "^2.0.0", "@ngneat/elf": "^1.5.6", "@ngneat/elf-cli-ng": "^1.0.0", "@ngneat/elf-devtools": "^1.2.1", "@ngneat/elf-entities": "^4.3.0", "@ngneat/elf-pagination": "^1.0.0", "@ngneat/elf-persist-state": "^1.1.1", "@ngneat/elf-requests": "^1.1.2",
I solves my issue, my core module was imported by a lazy loaded module, so the effects were registered twice,
HI, i have an issue when I dispatch action, the action is dispatch twice.
Here how I declare actions and effects
actions.ts
effects.ts
core.module.ts
core.module.ts
I can't reproduce this behaviour with a clean slate project.
If someone have hint let me know.
PS:
OS: Mac OS 12 NPM packages: