2

I’m using Redux to manage the logic of a music player. When it’s playing something and suddenly fails, I want to be able to intercept that action and make the player start again, but only n times, as I don't want to keep automatically retrying forever if the problem isn't recoverable.

I think a middleware is a good option to implement this, but I’m wondering if I should store the number of retries already done for a certain item either in the global state, or locally in the middleware.

A more general question would be if a Redux middleware should contain any local state at all, I mean, state that only it cares about.

Daniel
  • 683
  • 1
  • 9
  • 20
  • The general philosophy about redux is single application state, so I think it would go against that philosophy to store local state for middleware in most cases, unless those were intermediate values, or maybe cache values, but weren't relevant to the end 'application state'... – Garrett Motzner Aug 10 '18 at 15:41
  • A good rule of thumb would be, if I persist the redux state and then reload it, will my middleware (with a fresh internal state) work exactly the same as if that hadn't happened? If you can do that, then it might make sense to keep that internal state. Otherwise, it is probably application state. – Garrett Motzner Aug 10 '18 at 15:45

1 Answers1

1

In my opinion (and based on limited information about your specific project):

  • you should configure the n value via the middleware constructor, and
  • you should store both n and totalRetries in private variables of the middleware.

Why?

Choosing where to store your middleware state shares similarities with deciding between component state and redux state. You can use the same questions as a guide:

  • Do other parts of the application care about this data?
  • Do you need to be able to create further derived data based on this original data?
  • Is the same data being used to drive multiple components?
  • Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?
  • Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?
  • Do you want to keep this data consistent while hot-reloading UI components (which may lose their internal state when swapped)?

As Dan Abramov said:

The way I classify it is when ever state needs to be shared by multiple components or multiple pages and we need to persist some data over route changes, all that data should go inside the redux store.

You can map this idea from components to middleware by rephrasing "…persist some data over route changes…" to "…persist some data over [insert relevant boundary here]…", for example closing and relaunching the app.

  1. The totalRetries doesn't seem to represent a meaningful state of the application. It has to do with some "behind-the-scenes" I/O operation that wont persist across closing the app or sharing app state with a debugger. One might even argue that you should not expose it to other components via redux state, lest they rely on (potentially shifting) internal workings of your middleware.

  2. redux-saga, etc allow us to write this type of functionality in a very neat and testable way without having "exposed wires" in the application state or module namespace. You could use a saga to encapsulate your entire "play audio" behavior.

  3. Exporting a reducer and then accessing its compartmentalized "public" state from your middleware introduces quite a bit of unnecessary complication.

  4. If you want to expose totalRetries and n to other components, you may do so via an action, such as PLAY_AUDIO_RETRY with the action containing both variables in its payload.

jchook
  • 6,690
  • 5
  • 38
  • 40
  • 1
    Thanks for your answer, this was long time ago but I came with the same approach you describe, because as you comment, the rest of the app doesn't care about the number of retries. – Daniel Sep 01 '19 at 20:59
  • I have a similar problem/question that I can't seem to figure out, but writing this answer got me a lot closer! So thank you for asking the question. – jchook Sep 02 '19 at 16:15
  • Cool, share it here if you like, even if you have already solved it, so we can learn together. – Daniel Sep 02 '19 at 20:02
  • Honestly I have the hardest time figuring out the right question to ask. – jchook Sep 02 '19 at 22:14