0

May be there is a different way to solve it (like create another selector especially for errors)

    export const getMSFState = createFeatureSelector<STState>('st');
    export const selectCreatedSensor = createSelector(
        getMSFState,
        selectAllSensors,
        (state: STState, sensorArray: SeasonTotals[], props: { blockId: number}) => {
            const isSuccessCreate = state.action === fromST.ST_ADD_ELEMENT && !state.loading && !state.error;
            const isAnErrorCreate = state.action === fromST.ST_ADD_ELEMENT && !state.loading && state.error;
            if (isAnErrorCreate) {
                return throwError('Error X');
            }
            const blockSensors = [...sensorArray].filter(s => s.blockId = props.blockId);
            return isSuccessCreate ? blockSensors.pop() : null;
        }
    );

I want to catch the second subscribe parameter (the error one), but I don't know if is possible to emit it in the NgRx selector

    this.store.pipe(
        select(STSelectors.selectCreatedSensor, { blockId: this.id })
    ).subscribe((data) =>  {
        console.log('success creation', data);
    }, (error) => {
        // I can't catch this, instead I catch it in the first subscribe parameter (in the success one)
        // And the value emited is an Observable object
        console.log('something went wrong', error); 
    })
Vy Do
  • 46,709
  • 59
  • 215
  • 313
Daniel Delgado
  • 4,813
  • 5
  • 40
  • 48
  • We're going to need more details. Exactly what do you want to happen? – GregL Nov 21 '19 at 18:48
  • 1
    nope this is not possible a `selector` does not return an observable it always returns a value, therefore you cannot use `throwError` – Nickolaus Nov 21 '19 at 19:17
  • Thanks @Nickolaus, so what do you recomend to do as an alternative?. Maybe create another selector for catch errors? – Daniel Delgado Nov 21 '19 at 19:21
  • ... it would be intresting what happens if you `return new Error('xxx')` instead of `throwError` – Nickolaus Nov 21 '19 at 19:59
  • My advice would be to create an error property and use your reducer to fill that property if X condition has been achieved. If you need to do this across state (Combining different parts of state) then use an effect to update the reducer. – Nico Nov 22 '19 at 03:33

2 Answers2

2

Selectors are pure functions, I find it a bad practice to throw errors in selectors (and reducers). You should instead return a value (null, undefined, empty object, ...).

timdeschryver
  • 14,415
  • 1
  • 19
  • 32
  • What would you do in the case you had logic you wished to re-use in a selector, such as a function that does conversion but this logic could throw an Error, and then the selector could catch the Error so there is no side effect in order to return a default value such as your suggestion. Here it may be a desire to log the error using a service (but also services are not injectable in selectors AFAIK), but of course then its not a pure function. Is there any advice on how to transform this kind of case to be pure? Sorry if I'm hijacking the original question... I can open another if need be. – jamiebarrow May 16 '23 at 18:45
  • Further to the above, I can imagine that instead of throwing an Error, using a return type to indicate an error, similar to an Optional / Maybe pattern in pattern matched / functional languages could be useful, and then instead of try/catch, its a matter of selecting the value or a default value? – jamiebarrow May 16 '23 at 18:49
0

Selectors are always returning partial information about the current state of the store not a stream.

throwError returns an error to an observable stream and so you cannot return an actual state.

... basically what will happen is you will get something like Observable<Observable<Error>> when the throwError function in the selector returns, therefore this will not work.

...

First: You are using the store wrong, you should:

  • dispatch a getData event
  • in case of error: an getDataError event
  • in case of success an getDataSuccess event

My approach would be creating an exclusive loadingSelector loadedSelector and errorSelector wrap the data-loading in a service or facade or whatever you want to call it and do the error handling there so you can subscribe properly in your components:

getData(): Observable<MyData> {
   return this.store.select(selector.loaded)
     .pipe(
       switchMap(loaded => {
          if (loaded) {
             return this.store.select(selector.myData);  // if data already loaded return it
          }
          this.store.dispatch(loadingAction);  // dispatch loading event
          return this.store.select(selector.loading) // select the loading state
       })
       filter(loading => !loading), // wait until loading changes to false
       switchMapTo(this.store.select(selector.error)), // get current error state
       switchMap(error => { 
          if (error) {
            return throwError(error); // throw error if exits
          }
          return this.store.select(selector.myData); // otherwise return data
       })
     }
}
Nickolaus
  • 4,785
  • 4
  • 38
  • 60