I've been using Flux first and Redux later for a very long time, and I do like them, and I see their benefits, but one question keeps popping in my mind is:
Why do we decouple actions and reducers and add extra indirections between the call that will express the intent of changing the state (action) and the actual way of changing the state (reducer), in such a way that is more difficult to provide static or runtime guaranties and error checking? Why not just use methods or functions that modify a state?
Methods or function will provide static guaranties (using Typescript or Flow) and runtime guaranties (method/function not found, etc), while an action not handled will raise no errors at all (either static or runtime), you'll just have to see that the expected behavior is not happening.
Let me exemplify it a little better with our Theoretical State Container (TSC):
- It's super simple
- Think of it as React Component's state interface (setState, this.state), without the rendering part.
So, the only thing you need is to trigger a re-render of your components when the state in our TSC changes and the possibility to change that state, which in our case will be plain methods that modify that state: fetchData
, setError
, setLoading
, etc.
What I see is that the actions and the reducers are a decoupling of the dynamic or static dispatch of code, so instead of calling myStateContainer.doSomethingAndUpdateState(...)
you call actions.doSomethingAndUpdateState(...)
, and you let the whole flux/redux machinery connect that action to the actual modification of the state. This whole thing also brings the necessity of thunks, sagas and other middleware to handle more complex actions, instead of using just regular javascript control flows.
The main problem is that this decoupling requires you to write a lot of stuff just to achieve that decoupling: - the interface of the action creator functions (arguments) - action types - action payloads - the shape of your state - how you update your state
Compare this to our theoretical state container (TSC): - the interface of your methods - the shape of your state - how you update your state
So what am I missing here? What are the benefits of this decoupling?
This is very similar to this other question: Redux actions/reducers vs. directly setting state
And let me explain why the most voted answer to that question does not answer either my or the original question: - Actions/Reducers let you ask the questions Who and How? this can be done with the our TSC, it's just an implementation detail and has nothing to do with actions/reducers themselves. - Actions/Reducers let you go back in time with your state: again this is a matter of implementation details of the state container and can be achieve with our TSC. - Etc: state change orders, middleware, and anything that is currently achieved with actions/reducers can be achieved with our TSC, it's just a matter of the implementation of it.
Thanks a lot! Fran