Skip to content

Instantly share code, notes, and snippets.

@jsonberry jsonberry/readme.md
Created Nov 27, 2019

Embed
What would you like to do?
Injecting Actions Service for profit

Reference to this twitter thread: https://twitter.com/jsonberrry/status/1199566382716731393

Subject: "injecting the actions service into the component level as a means for propagating success/error states to a presentational layer"

Question from @jdpearce

"“Injecting the actions service...” woah, woah, woah...what?!

Why aren’t you setting a property in the store and selecting that?"

TL;DR answer: it depends, and if I don't need to store scalar information and can derive the information necessary from just signals flowing down an Observable stream, that alleviates some micromanangement and keeps me aligned to that often seen "don't store what you can derive" principle

Answer:

The best use case I have for this is for optimistic updates. Here's a contrived example:

Scenario: A "like" button on a post, you can "like" a post and also remove your "like". Also if it fails, we'll get a toast popup that says you can try again.

When using an event sourcing / CQRS pattern we might get a 201 from the API when interacting with the like button. That represents that the backend has received and accepted our intention of changing state, but the backend process has not fully finished yet, instead the API is telling us, "OK, cool, thanks we'll get to that."

In keeping with the "don't store what you can derive" principle often occuring in FRP, I only need to know that there was an OK or a NOT OK signal, I dont' have to store that as scalar data in a state tree, I just need the signal.

In the presentation layer, if the ngrx Action service is injected, I can set the "likedness" of a like button as an Observable<boolean> by using the action service:

postLiked$ = merge(
  this.actions$.pipe(
    ofType(postLikedRequestAccepted),
    mapTo(true),
  ),
  this.actions$.pipe(
    ofType(postUnlikedRequestAccepted),
    mapTo(false)
  )
).pipe(
  startWith(false),
)

Also in the presentation layer, or inside of an Effects class, I can utilize a toast service if the API request failed for some reason

// maybe the showing of the toast is an Observable<boolean> too,
// this is just a contrived example 

postInteractionFailedEffect$ = this.actions$.pipe(
  ofType(postInteractionFailed),
  tap(() => this.toastService.getToasty())
)
@jsonberry

This comment has been minimized.

Copy link
Owner Author

jsonberry commented Nov 27, 2019

Now, holding some error state in the state tree is absolutely something I do all the time, but it just isn't necessary in every scenario.

There's definitely been a buncha discussion about this topic on twitter before. I think that @BioPhoton and @timdeschryver both have opinions about it.

@jsonberry

This comment has been minimized.

Copy link
Owner Author

jsonberry commented Nov 27, 2019

Sorry in the example for the "likedness" I forgot to include actual state from the backend first, so it would like more like this:

.. also, please excuse this code, I'm not currently trying to write a real robust example, I'm taking care of my baby and he's sleeping right now ;)

postLiked$ = merge(
  this.store.pipe(
   select(postLiked), // presumebly this fires first and is based on state data from the backend
  ),
  this.actions$.pipe(
    ofType(postLikedRequestAccepted),
    mapTo(true),
  ),
  this.actions$.pipe(
    ofType(postUnlikedRequestAccepted),
    mapTo(false)
  )
)

Other ways of handling an optimistic update would be to do work in the reducers, but there are plenty of times when injecting the actions service and using signals coming down as a source of truth is perfectly "valid", as it'll fit into the principles of FRP as we've seen it proliferate in the Angular/ngrx community

@BioPhoton

This comment has been minimized.

Copy link

BioPhoton commented Nov 28, 2019

Error, succes, loading are metha information.

Moreover this "state" is ephemeral.

Based on that i strictly avoid putting that in the store. It worst practice.
You can see why easily as you always are forced to add extra logic based on the lifetime of something else (component, directive, http requests)

Sounds over strong, but other things are just super wrong and mess up everything.

This is part of local state management wich is a different animal that solves different problems.

So my opinion...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.