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.
In my opinion (and based on limited information about your specific project):
n
value via the middleware constructor, and 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.
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.
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.
Exporting a reducer and then accessing its compartmentalized "public" state from your middleware introduces quite a bit of unnecessary complication.
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.