Why not both?
Actions and reducers should be as simple as possible.
This is easier said than done, but by introducing other concepts and patterns, such as a selector , sagas and even simple utility functions / classes , complexity can be significantly reduced for both actions and gearboxes.
Actions
I know that the term “broadcast” is really puzzled in the world of streams, but I find that it helps me clarify what should belong to my actions. The action should "translate" into the world what has just happened - he personally does not care about what should be observed, or about how many gearboxes prefer to react to it - his is simply a messenger. i.e.: it doesn’t matter how the application looks after the specified action.
My opinion is that business logic / rules either belong here directly, or can be routed here through auxiliary service functions. See the sections on async and the utility below.
He must describe what happened. Describe, describe, describe
Adapters
Gearboxes should form a complete (ish) representation of your application with a minimum amount of data. Whenever possible, keep your state tree normalized to make sure you keep the minimum state.
My opinion is that they should be as light as possible, and at best it should be just basic manipulation of objects - adding / removing keys or updating values.
What should the state look like on the basis of the description that I just received? (from action)
An approach
It sounds crazy, but debugging a rubber duck (in this case, programming a rubber duck) really helps in planning the structure of the reduct.
I will literally say (sometimes even outloud) through steps, for example:
- Hover over the message heading and click edit
- The application goes to the "Edit Message" page.
- You can edit the header field (and the field will be updated)
- You can edit the body field (and the field will be updated)
- The application will save every 90 seconds as a draft, the save icon will be displayed in the upper right corner during automatic saving, but you can continue to work
- You can also save by clicking the save button at any time. During this time, you will not be able to edit, and you will be redirected to the index page.
This can be easily translated into actions and gears by looking at what describes and what is, as a result, a state change:
- Click "Edit": "Action". We describe for the application in which the entry with the identifier X was clicked.
- Switches to the "Edit Message" page: Reducer. When we “hear” “message editing,” we change the state of the application so that it now displays the message editing page.
- Change name / body: Action - We describe which field and what value is entered by the user.
- Update name / body: Reducer - The application state has been changed in accordance with the entered fields in
- Auto save: action / gear / action / gear
- Action: Describe the application in which autosave started.
- Gear: The state of the application changes to indicate that autosave is in progress.
- Action: describe completed autosave
- Reducer: the state of the application is changed to hide the autosave operation.
- etc.
What I try to do, although for a long time from time to time, is that its not something that should be simpler, its where there are things (kind), but most likely, you will see that your actions in end up becoming more complex with your gearboxes to a lesser extent.
But my actions are still not subtle ..
All of the above is simple enough to say, but how do you keep your business activities clean / slim / light / simple / etc?
At the end of the day, most business logic has little to do with React / Redux - so you can usually dig it into your utility functions or classes. This is great for several reasons: a) it’s easier to check because it does not have a zero binding to React / Redux and b) it keeps your actions light, and c) it is more encapsulated - with minimal knowledge about reaction / reduction - this is not bad.
All this is just Javascript at the end of the day - there is nothing wrong with importing custom business classes or utility functions.
But but what about async ..
Async usually begins to clutter up actions very quickly. The saga is really worth a look if you want to clear your actions, but enter a certain learning curve if you are not familiar with generators and something else. In this case, thunks are still useful and remember that you can combine several asynchronous functions into one promise that thunk can play (which again, that a grouped promise can be divided into a utility function class, for example generateSaveSequencePromise() , which can wrap 4 or 5 asynchronous samples and return one promise).
Lateral note - you should minimize sending several times from one stream, as in the first example. If possible, try creating a parent action that combines the entire action into one. Therefore, using your first example, this could be:
// action dispatch({type: "FETCHING_USER_IMAGE", id: userId });
And then your various reducers should clear their message queues or something else if they "hear" this type.