4
votes

Say you create a new Book, by dispatching a CREATE_BOOK action from a BookComponent. In an effect, we are handling the http save, and after that we pass the created Book to the reducer through a CREATE_BOOK_SUCCESS action. So far so good.

But how would one get the ID of the newly created Book in the BookComponent that spawned the CREATE_BOOK action? I can only think of workarounds:

  • Store the created book in the state, in a field lastCreatedBook. This doesn't seem scalable if I have a lot of different items. Also, I would have to clean that field when the component is destroyed.
  • retrieve the complete list of books in the BookComponent. The last one is the created Book.
  • Don’t use ngrx/effects. Just call the BookService.create() method and subscribe to it. Dispatch the CREATE_BOOK_SUCCESS action from there. But then why use effects in the first place?
2
Not sure what is your user interface design looks like - typically after clicking "Add", one may go back to "List" page. The list will typically have newly added book.Wand Maker
The UI would be something like a page where one could give his favorite book in a popup. The popup offers the choice to select an existing book, or create a new one. After creating the new one, the original page should have a reference to the new book.David Bulté
In the scenario you describe, how would one know what is the created book (say to highlight it). It’s the same problem, no?David Bulté
I see. If you want to highlight the recently added book in the book list - then, yes, you need a way to tag that book as new one - if you can add created_time to book, then, may be that can help you with picking latest one. Your second option or some variant of it is best option.Wand Maker

2 Answers

2
votes

I've been facing that situation too and you've got in my opinion 2 options there:

  • first one and definitely the most simple, you create the ID directly on the frontend using UUID library, but maybe that your IDs are being generated on the backend and you can't do that
  • second option, a bit less forward but that's working great too:

Generate an ID and pass it within the payload of the action (in addition of your current payload if needed). You might call it for example actionId.

From your effect, when you map to the action CREATE_BOOK_SUCCESS, also pass that ID you've got access to! (because we're in the piece of code that's handling the CREATE_BOOK action).

From the (smart) component, you can subscribe to actions, just like you do within your effects! And so you can do something like that:

class MyComponent {
  // ...

  createBook(book: Book) {
    // first, generate a unique ID to track the action
    // you can call directly the uuid function or even better create
    // a uuidService that you inject but for simplicity here I'm not doing it
    const actionId: string = uuid();

    // then watch for an action of type CREATE_BOOK_SUCCESS with that ID
    this.actions.pipe(
      ofType<BooksActions.CreateBookSuccess>(BooksActions.CREATE_BOOK_SUCCESS),
      filter(({payload}) => payload.actionId === actionId),
      first(),
      tap(({payload}) => {
        // do what you want here
        // you've got access to the payload of the CREATE_BOOK_SUCCESS action where
        // you have the book returned from the backend (I think)
      })
    );

    // finally, dispatch the action
    // it's important to do it after you've subscribed to actions otherwise it might happen
    // too fast and you wouldn't get the notification
    this.actions.dispatch(new BooksActions.CreateBook({ ...book, actionId }));
  }

  // ...
}

Note that I'm using first here when subscribing to the action so even if you add 10 books reaaaally quickly you'll have 10 different subscriptions which is totally fine.

2
votes

You can't directly let the component know the ID that has been created, because you have to follow the unidirectional data flow of redux.

If you really want to use the ID somewhere you can store this inside your store state as you mentioned, or create the ID client side.

If you want to highlight the created book somewhere in a list you can make implement the trackBy function of the *ngFor directive and create an animation on the newly created item.

For more effect usages, check out Start using ngrx/effects for this