I have at least two approaches to solve a problem.
Repreform calculations inside views on each update (map, filter, find)
Keep an "CurrentState" object inside my redux state.
I choose to create a CurrentState object that stores my calculated result (It calculates the results inside the reducer function).
Approach 2 saves processor calculations, but it feels dirty.
is it considered an antipatern to have a CurrentState object (updated from the reducer) inside the state?
Thank you in advance!
Not totally clear from your question - but I assume that you are talking about situation, that you can compute "result" from other data in your store and asking if storing it (redundantly) in store is good pattern.
Well, generally I would not recommend it as keeping redundant data in store can lead to inconsistent data bugs - if you forgot to keep your data in sync (e.g. when you or someone else working on the project would change one data and forget to change the order place).
The standard patter for this is usually to use selectors and memonization (see https://github.com/reactjs/reselect). The idea is to keep only normalized data in your store but to use selectors in your views. Selectors are responsible to "cache" (memonize) extensive computation. Another advantage of selectors is that it provides level of indirection for your application and you can easily evolve your state (as the project grows) without affecting your whole application. On the other hand for small and one time projects selectors can be just "another" boilerplate - but in general I recommend to use them.
This was a hard anti-pattern.
If you are reading this chances are you don't fully understand redux and I would like to recommend
https://github.com/leoasis/redux-immutable-state-invariant
I have been following the widely given advice of learning React development by first mastering component props, encapsulating UI state in component level this.state and passing it down selectively through the component tree. It's been an enlightening experience. I've come to appreciate the power of the stateless view design pattern and I feel that I've been able to achieve robust and well organized results using these techniques.
Moving on, I am now trying to incorporate more sophisticated state management using redux. But as I wade through the complexity and integrate redux into my apps, I find myself confronting the following observations about how my code has evolved. Some of these developments seem sensible, but others make me question whether I'm doing things 'right'.
1) Action Creators as the nexus of business and UI logic
I find that much of the logic that was previously implemented in the React lifecycle functions componentDidUpdate etc., and in onTouch/onPress handlers, is now implemented in action creators. This seems to be a positive development as it keeps 'everything in the same place' and allows for unit testing.
Question: Is it best practice to concentrate business logic in a web of fairly intricate action creators?
2) Hollowed out reducers
As a corollary to #1 above, I find that my reducers and their corresponding action objects have evolved into a de-facto list of setters that do little more than update the state store with the passed along values, in this fashion:
case types.SAVE_ORDER:
return Object.assign({}, state, {
order: action.order,
});
A big part of the reason for this is that reducers are supposed to be pure functions and therefore I'm limited in what I can do with them (e.g. no async processing). Additionally, reducers are allowed only to operate on their respective sub-section of the store state. Given that much of my app's complexity already necessarily resides in the action creators, I find it hard to justify arbitrarily migrating complexity into reducers simply for the sake of making them 'look useful'.
Question: Is it normal, and acceptable practice to have boilerplate reducers that function merely as glorified setters to the redux store state?
3) redux-thunk everywhere
I've asked separately on SO why redux-thunk is even necessary (as opposed to calling standard action creators inside of async callbacks/utility functions). I've been pointed to this answer by Dan Abramov which provides a very satisfactory explanation (vis-a-vis scalability, server side rendering and myraid other reasons).
Having accepted the necessity of redux-thunk, I find that the majority of my action creators need to perform async actions, need access to getState, or dispatch multiple changes to the state. As a result I've been returning 'thunks' extensively.
Question: Is it normal for a redux application to rely extensively on thunk'ed action creators, and rarely to fire a standard object action directly?
4) Redux as global this.state
In the final analysis, it seems my app's redux store has evolved to effectively resemble a global this.state. You could think of it as keeping the entire application state in this.state in the outermost container component, but without the inevitable mess that comes with passing the said state down through nested layers of props, and any changes back up the component tree through a rats-nest of handler functions.
Question: Is redux the correct tool to use for a global state store? Are there alternatives out there that behave more akin to react's built-in this.state, allowing a global application state to be propagated through stateless react components, and updated from throughout the application via a centralized 'switchboard', without the seemingly endless web of boilerplate, constants and switch statements that come with adopting redux?
5) One single action type?
This follow up question is inspired by one of the posted comments.
Question: Could one legitimately (in all seriousness, not just blatantly demonstrating a point) use redux with precisely one action type?
Example - Action creator:
export function someActionCreator(various_params){
return (dispatch, getState => {
// ... business logic here ....
asyncIfThisIfThat().then(val => {
dispatch({
// type: 'UPDATE_STATE', // Don't even bother setting a type
order: val
})
})
)
}
The one universal reducer case:
export default function app(state = initialState, action = {}) {
return Object.assign({}, state, action)
// Just unconditionally merge into state!
}
Seems to me this would provide a globally scoped state object that is automatically mapped to connected components, and one that benefits from all the advantages of immutable state and interoperable with React props. In this scheme, dispatch effectively becomes a global setState.
Note - Please don't take this question wrong - this is certainly not criticism of redux. As a learner I am obviously in no position to judge a technology backed by the expertise of thousands and the support of millions. I have no doubt of its value in the right context.
I'm just sensing the smell of a questionable pattern in my own code and wondering what, if anything I'm doing wrong, or whether I'm using the right tool for the task.
My answer is mostly speaking from my own experience learning redux and using it professionally. The team I was on went down the same path of setter-like actions, and then shifted away to action names that were more event-based and describe what had happened rather than what should happen.
Question: Is it best practice to concentrate business logic in a web of fairly intricate action creators?
This depends on how your actions are named. In your case, your actions are very glorified setters, so all of your business logic is going to live inside of Action Creators. If you name your actions to be more event-like (descriptive about what happened) rather than setters, you're going to have some of the business logic shift into the reducers, and complexity removed from action creators, because event actions naturally feel more re-usable across different reducers. When you do setter actions, the tendency is to have setter-actions that interact with only 1 reducer, and create more setter-actions when you want other reducers to be involved.
If you have an app for a school, and a student is expelled, you'll likely dispatch a REMOVE_STUDENT and then aDELETE_GRADES_FOR_STUDENT action. If your action has an event-like name, you may be more inclined to just have a STUDENT_EXPELLED action that the grades reducer and student roster reducer both act upon it.
There is nothing technically stopping you from having setter-like names, and acting on them in multiple reducers, though. It's just not the tendency that my team fell into when working in Redux and using setter-like names. We didn't want to muddy up the expectations and purity that came from having concise action names, where the impact on state was very clear. REMOVE_STUDENT_GRADES and DELETE_STUDENT_FROM_ROSTER felt good.
Question: Is it normal, and acceptable practice to have boilerplate reducers that function merely as glorified setters to the redux store state?
It is normal, but not necessarily correct. This is how our codebase grew initially - we even had standards to name our actions as RESET_..., SET_..., REMOVE_..., ADD_..., UPDATE... etc. This seemed to work for a while, until we bumped into cases where we needed multiple reducers to update according to a single action.
You actions will evolve in one of these 2 ways (or both)
Dispatch multiple actions in a row (you must use a library like redux-batch-actions if you want to dispatch multiple actions in a row). We chose not to use this, because it's cumbersome and didn't feel like it scaled very well as our codebase grew in size.
Rename your actions to be more generic and re-usable across different reducers. This is what we ended up doing. Having actions as setters and getters was cumbersome. Dan Abramov and others has expressed their opinion that Redux Actions should be events (a description of a thing that has happened), rather than instructions (a description of a thing that should happen). The team I work on agreed with this, and we've moved away from the setters-style of actions. There was much debate about this earlier on when Redux was new.
In scenario 1, you might do something like this:
// student has been expelled from school, remove all of their data
store.dispatch(batchActions(
removeStudentFromClass(student),
removeStudentsGrades(student)
));
// student roster reducer
case REMOVE_STUDENT_FROM_CALLS:
/* ... */
// student grades reducer
case REMOVE_STUDENT_GRADES:
/* ... */
If you go down this path without using Batch Actions, it's an absolute nightmare. Each dispatched event will recompute state, and re-render your app. This falls apart very quickly.
// student has been expelled from school, remove all of their data
store.dispatch(removeStudentFromClass(student));
// app re-rendered, students grades exist, but no student!
store.dispatch(removeStudentsGrades(student));
In the above code, you dispatch an action to remove the student from class, and then the app re-renders. If you have a grades page open, and you can see the students grades, but the student is removed, you're very likely going to reference state in the student roster reducer to grab the student info and that can/will throw a JS error. Bad news. You have the grades for a student of undefined?! I ran into issues like this myself, and it was part of our motivation for moving to option 2 below. You'll hear about these kinds of states called "intermediate states" and they're problematic.
In scenario 2 your code might look more like this:
store.dispatch(expelStudent(student));
// student roster reducer
case EXPEL_STUDENT:
/* ... */
// student grades reducer
case EXPEL_STUDENT:
/* ... */
With the code above, the student is expelled via the action, and their data is removed from all reducers in 1 step. This scales nicely and your codebase reflects the business terms related to your app that you would talk about day-to-day. You can also perform the same state updates for multiple events if it makes sense from a business logic perspective:
case EXPEL_STUDENT:
case STUDENT_DROPPED_OUT:
case STUDENT_TRANSFERRED:
/* remove student info, all actions must have action.payload.student */
Question: Is it normal for a redux application to rely extensively on thunk'ed action creators, and rarely to fire a standard object action directly?
Yes definitely. As soon as you need to grab a little piece of data from the store in an action creator, it has to become a thunk. Thunks are very common, and should have been part of the redux library.
As our thunks grew in complexity, they got confusing and difficult to easily understand. We started to abuse promises and return values and it was taxing. Testing them was also a nightmare. You have to mock out everything, it's painful.
To solve this problem, we pulled in redux-saga. Redux-saga is easily testable with libraries like redux-saga-test-plan or redux-saga-test-engine (we use test-engine and have contributed to it as needed).
We aren't 100% sagas, and don't aim to be. We still use thunks as needed. If you need to upgrade your action to be a little smarter, and the code is very simple, there's no reason why you shouldn't upgrade that action to a thunk.
As soon as an action creator gets complex enough to warrant some unit testing, redux-saga might come in handy.
Redux-saga does have a rough learning curve to it, and feels quite bizarre at first. Testing sagas manually is miserable. Great learning experience, but I would not ever do it again.
Question: Is redux the correct tool to use for a global state store? Are there alternatives out there that behave more akin to react's built-in this.state, allowing a global application state to be propagated through stateless react components, and updated from throughout the application via a centralized 'switchboard', without the seemingly endless web of boilerplate, constants and switch statements that come with adopting redux?
MobX - I've heard good things about it from people who have your same complaints about Redux (too much boilerplate, too many files, everything is disconnected) I don't use it myself and have not used it, though. There's a good chance that you'll enjoy it more than Redux. It solves the same problem, so if you actually enjoy it more then it may be worth the switch. Developer experience is very important if you're going to work on code for a long time.
I'm okay with the Redux boilerplate and whatnot. The team I worked on has made macros to scaffold out the boilerplate of creating new actions, and we have lots of tests in place so our Redux code is pretty solid. Once you work with it for a while, you internalize the boilerplate and it's not as exhausting.
If you do stick with Redux long term, and are savvy enough to adopt flow on top of redux it's a huge win for long-term maintainability. Fully-typed redux code is amazing to work in, especially for refactoring. It's so easy to refactor a reducer/actionCreators, but forget to update unit test code. If your unit tests are covered by flow, it's going to complain that you're calling a function incorrectly immediately. It's wonderful.
Introducing Flow is a complex hurdle to get over, but well worth it. I wasn't involved in the initial set up, and I assume it's gotten easier to introduce to a codebase, but I'd imagine that it will take some learning and hours. Worth it though. Definitely 100% worth it.
Question: Could one legitimately (in all seriousness, not just blatantly demonstrating a point) use redux with precisely one reducer?
You definitely could, it could work for a small app. It wouldn't scale well for a larger team, and refactoring seems like it would become a nightmare. Splitting the store up into separate reducers lets you isolate responsibilities and concerns.
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!
Try using my lib redux-light. You'll need to use store.setState and store.getState
similar to React.Component.setState without reducers at all, and without other boilerplate.
Question about Redux data flow.
Let's talk about huge enterprice application. Dozens of modules, complex hierarchy of reducers, hundreds of action-types.
Simple flow:
Control dispatches an action(for example, input - typing). This action go through every reducer, go through hundreds of switch-cases, and new state is merged from all of reducers with minimal changes. I think, that we have huge unnecessary overhead in this scenario.
What options we can use to decrease overhead?
Use isolated high-level sub-apps with their own provider.
This option will decrease overheads. But if we need any common features in sub-apps, like account info/notifications/etc, we should duplicate it.
Use asyncReducers for code-splitting.
This option, also will decrease overheads, but it is not recommended.
Make a reducers with action-filters. In this case, we add additional information to each action, which reducer should process it.
This option also decrease number of swithces and complexity of newState merging.
But in option 3 I can't understand one thing.
We have control, which is connected to concrete part of state.
99% of actions are processed by single reducer.
Each action at reducer is processed by "case function", which is moved to separate logic-module.
We have action-creator which knows concrete reducer which process this action, and has access to global state.
Why action-creator should dispatch global action, which then will filtrated by cascade of reducers, and then will be swithed through dozens of cases, with not optimal merging of new global state?
Why can't we call case-function in action-creator, compute new global state in optimal way and dispatch it as payload with "SET_GLOBAL_STATE" action type?
I understand that it is anti-pattern. But i can't understand what we lose in this case.
I'll be glad, if someone will explain me what is wrong.
Several thoughts here.
First, per the Redux FAQ entry on splitting logic between action creators and reducers, it's up to you where to put the bulk of your logic. If you prefer to do the actual calculations for what a piece of the new state should be in an action creator, and just have a given slice reducer do return {...state, ...action.payload}, you can.
Second, per the Redux FAQ entry on the performance of calling reducer functions, the cost of calling many reducer functions will generally be very minimal, since most of them will just check the action type and return their existing slice of state. So, the reducer logic will rarely be a performance bottleneck.
Third: yes, it's entirely possible to calculate the entire new app state in an action creator, and have a single generic "SET_GLOBAL_STATE" action. This will allow use of Redux's middleware and time travel debugging, etc. However, this also throws away many of the possible benefits and use cases for Redux. I discussed a lot of the intended purpose for Redux in my blog posts The Tao of Redux, Part 1 - Implementation and Intent and The Tao of Redux, Part 2 - Practice and Philosophy.
To summarize the reasons why we discourage the idea your describing: Redux is intended to make it easy to trace when, why, and how a certain piece of state was updated. While it's up to you to decide how granular you want your actions to be, ideally they should be semantically meaningful. That way, when you read the action history log, you can actually understand the sequence of actions that were dispatched. Also, if you search for a given action type, you should be able to quickly narrow down exactly where in the codebase that action is used. If you only have a single "SET_GLOBAL_STATE" action, it will be almost impossible to determine what part of the code caused a given state update.
I have watched a few videos about reducers, and they all claimed that I should use only one reducer and state for my whole project and I should combine every reducer into one big reducer.
Now my question is, why would I do this? Imagine I have a big application and I combine all reducers. My combined reducer would be huge and a single state change would take quite long since we need to check every single reducer slice.
Should I really just use one reducer for a bigger project? Why should we combine them, instead of creating multiple stores, and what about the performance?
As your app grows more complex, you'll want to split your reducing
function into separate functions, each managing independent parts of
the state.
.combineReducers(...)
Thanks a lot.
Per the Redux FAQ entry on the performance of "calling all reducers":
It's important to note that a Redux store really only has a single reducer function. The store passes the current state and dispatched action to that one reducer function, and lets the reducer handle things appropriately.
Trying to handle every possible action in a single function does not scale well, simply in terms of function size and readability, so it makes sense to split the actual work into separate functions that can be called by the top-level reducer.
However, even if you happen to have many different reducer functions composed together, and even with deeply nested state, reducer speed is unlikely to be a problem. JavaScript engines are capable of running a very large number of function calls per second, and most of your reducers are probably just using a switch statement and returning the existing state by default in response to most actions.
Also, from the FAQ entry on whether you should create multiple stores:
It is possible to create multiple distinct Redux stores in a page, but the intended pattern is to have only a single store. Having a single store enables using the Redux DevTools, makes persisting and rehydrating data simpler, and simplifies the subscription logic.
However, creating new stores shouldn't be your first instinct, especially if you come from a Flux background. Try reducer composition first, and only use multiple stores if it doesn't solve your problem.
I have a saga which runs once every 10 seconds on a POLL action to updated the current state on my GUI.
when a POLL happens I need to make a few calls to walk down the rest interface to get to find the components I care about. There will be a total of 1-5 components, for each of these I need to make a separate rest call for Foo and Bar elements of the components.
Then at some point I need to do some summations, combining the Foo and Bar data together to have the structure expected by my table for listing components, calculating some totals across all components in my dashboard etc. None of the work is cpu intensive, but it adds up to a decent bit of code since I have so many things that need tweaked.
Currently I'm doing all of this in the Saga, but I'm not sure if this is considered bad practice? I feel like reducers are the general 'go to' place for data tweaking, but it feels odd throwing an action with such a large payload, all the responses from every call in a saga, since much of the rest response is data I don't care about. I also like doing all the processing in the saga so I can decide at the end of everything rather to pass an error action to show an error to the user or pass a success action which clears any previous errors, some of the decision for rather I want to clear the action requires more processing of the data.
My only concern is that the generator is getting rather large, with lots of helper methods that feel a little out of place in a saga class to do the processing (their need to be moved to a utils class no matter what I think). The processing isn't too expensive and I am using generators so I don't think the processing will have a noticeable affect on saga's 'threading'. Still, If there is a recommended best practice I want to stick to that. Am I breaking from standard practices doing all of my tweaking of the data in my saga and sending to the reducer a per-formatted object for it to store into the state without any other processing?
This is really a specific case of a common question that is addressed by the Redux FAQ on "where should my business logic live?". Quoting that answer:
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.
There's nothing wrong with having logic on the "action creation" side (whether it be in components, thunks, sagas, or middleware) that does a lot of work to prepare and format data, and having the reducer simply store what was included in the action. On the flip side, having more logic on the reducer side can mean that time-travel debugging will re-run more of your actual code, giving you more chances to edit and retry behavior.
Overall, it sounds like what you're doing is perfectly reasonable.