Search code examples
angularngrxngrx-entity

ngrx - where to make web requests to update store after successful action


I'm currently working with ngrx and nrg-entity. I wonder what the best practices are for updating the store after a successful web request if the response doesn't contain the entity object that have to be stored.

Example:

// My state
export interface State extends EntityState<Book> {}

I make a web request to create a book. From backend I only get an id of the created book. By default you create a reducer to put that new created book in your store like so:

createReducer(
  initialState,
on(BookActions.createBookSuccess, (state, { book}) =>
    bookAdapter.addOne(book, { ...state, creating: false })
  ));

I see two options to return the new book to the reducer:

  1. In the dataservice createBook() method you make a second call "GetBookById()" when you get the id of the new book and return the book object.
  2. In the createBookSuccess effect you trigger a dataservice call "GetBookById()" and return the result (to the reducer).

Is one of these options the way to move? Or is there a better way?

I hope I have been able to make my problem clear.

EDIT: I guess it's better to provide a practical example:

Actions:

export const createBook = createAction(
  '[Book] Create Book',
  props<{ book: NewBook }>()
);

export const createBookSuccess = createAction(
  '[Book] Create book Success',
  props<{ book: Book }>()
);

Effects:

createBook$ = createEffect(() =>
    this.actions$.pipe(
      ofType(AuftragActions.createBook),
      switchMap((action) =>
        this.bookDataService.createBook(action.book).pipe(
          map((response) =>
// what to do, to get the new book object from backend?
            BookActions.createBookSuccess({ book: response })
          ),
          catchError((error) =>
            of(AuftragActions.createAuftragFailure({ error }))
          )
        )
      )
    )
  );

Reducers:

on(BookActions.createBook, (state) => ({
    ...state,
    creating: true,
    error: null,
  })),

on(BookActions.createBookSuccess, (state, { auftrag }) =>
    bookAdapter.addOne(book, { ...state, creating: false })
  ),

Data service:

createBook(book: NewBook): Observable<Book> {
 // returns book id
}

loadByBookId(bookId: string): Observable<Book> {
 // returns book;

So, how to extend the effect "createBook$" to emit the createBookSuccess-Action with the new created book from backend?


Solution

  • There are a lot of ways you can do this but my instinct would be to have the effect trigger another action/effect that gets and sets the next value. So your first effect returns something like this,

    return [
      ...,  //Whatever it already returns
      LoadBookByIdEffect
    ]
    

    Then your LoadBookIdEffect triggers an Action that sets the book value in state,

    return SetBook({ book });
    

    Here is a more detailed explanation of the syntax for chaining effects together like this,

    Chain Actions in an Effect in @ngrx