I have been following the widely given advice of learning React development by first mastering component props
, encapsulating UI state in component level this
I'm a Redux maintainer. I'll give you some initial answers and point you to some learning resources, and I can answer further questions as needed.
First, Cory Danielson has given some excellent advice, and I want to echo pretty much everything he said.
Action creators, reducers, and business logic:
I'll quote the Redux FAQ entry on splitting business logic between action creators and reducers:
There's no single clear answer to exactly what pieces of logic should go in a reducer or an action creator. Some developers prefer to have “fat” action creators, with “thin” reducers that simply take the data in an action and blindly merge it into the corresponding state. Others try to emphasize keeping actions as small as possible, and minimize the usage of getState() in an action creator. (For purposes of this question, other async approaches such as sagas and observables fall in the "action creator" category.)
There are some potential benefits from putting more logic into your reducers. It's likely that the action types would be more semantic and more meaningful (such as "USER_UPDATED" instead of "SET_STATE"). In addition, having more logic in reducers means that more functionality will be affected by time travel debugging.
This comment sums up the dichotomy nicely:
Now, the problem is what to put in the action creator and what in the reducer, the choice between fat and thin action objects. If you put all the logic in the action creator, you end up with fat action objects that basically declare the updates to the state. Reducers become pure, dumb, add-this, remove that, update these functions. They will be easy to compose. But not much of your business logic will be there. If you put more logic in the reducer, you end up with nice, thin action objects, most of your data logic in one place, but your reducers are harder to compose since you might need info from other branches. You end up with large reducers or reducers that take additional arguments from higher up in the state.
I talked about this topic some more in my post The Tao of Redux, Part 2 - Practice and Philosophy earlier this year (specifically the sections on action semantics and thick vs thin reducers, and again in a recent Reddit comment thread.
Use of thunks:
Yes, thunks are a valuable tool for any complex synchronous logic that needs to live outside a component, including any code that needs access to the current store state. They're also useful for simple async logic (like basic AJAX calls with just success/failure handlers). I discussed the pros and cons of thunks in my post Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability.
In my own app, I use thunks in many places, as well as sagas for more complex async logic and workflows, and highly recommend thunks as a useful tool overall.
Redux as a global store
I've frequently said that you can use as much or as little abstraction on top of Redux as you want. You don't have to use switch statements, you can use lookup tables or any other conditional logic in your reducers, and you are highly encouraged to reuse reducer logic. In fact, there's dozens of existing utilities to generate reusable action creators and reducers, as well as many higher-level abstraction libraries written on top of Redux.
Use of a single blind-setter reducer
This is another topic I looked at in The Tao of Redux, Part 2, and someone else had a good comment another recent Reddit thread. It's certainly technically feasible to do so, but per that Reddit comment, your reducers don't actually "own" the state shape any more. Instead, the action creators do, and there's nothing stopping them from feeding in data that doesn't make sense for a given action type or reducer.
As I talked about in The Tao of Redux, Part 1 - Implementation and Intent, one of the key intents behind the creation of Redux was that you should be able to look at a log of dispatched actions and understand where/when/why/how your state was updated. While Redux itself doesn't care what the action.type
field actually contains, an action history log will make more sense to you (or some other developer) if the dispatched actions are meaningfully named. Seeing 10 "SET_STATE"
actions in a row tells you nothing useful about what's going on, and while you could look at the contents of each action and the resulting diff, an action type like "EXPEL_STUDENT"
means a lot more just by reading it. In addition, unique action types can also be traced to where they're used in specific places in the codebase, thus helping you isolate where the data is coming from.
Hopefully that helps answer some of your questions. If you'd like to discuss things further, I usually hang out in the Reactiflux chat channels on Discord evenings US time. Be happy to have you drop by and chat sometime!