React-Redux: 应该将所有组件状态保存在 Redux 存储中

假设我有一个简单的开关:

当我单击按钮时,Color 组件在红色和蓝色之间变化

我可能通过做这样的事情来达到这个结果。

Index.js

Button: onClick={()=>{dispatch(changeColor())}}
Color: this.props.color ? blue : red

容器 JS

connect(mapStateToProps)(indexPage)

Action _ create. js

function changeColor(){
return {type: 'CHANGE_COLOR'}
}

Reducer js

switch(){
case 'CHANGE_COLOR':
return {color: true}

但是,这是一个地狱的大量代码编写的东西,我可以在5秒钟内实现与 jQuery,一些类,和一些 css..。

所以我想问的是,我到底做错了什么?

33874 次浏览

Redux is primarily intended for "application state." That is, anything related to your application logic. The view built on top of it is a reflection of that state, but does not have to exclusively use that state container for everything it does.

Simply ask these questions: Is this state important to the rest of the application? Will other parts of the application behave differently based on that state? In many minor cases, that will not be the case. Take a drop down menu: The fact that it's open or closed probably won't have an effect on other parts of the app. So, wiring it up to your store is probably overkill. It's certainly a valid option, but doesn't really net you any benefits. You're better off using this.state and calling it a day.

In your particular example, does the color that button is toggled to make any difference in other parts of the application? If it's some sort of global on/off toggle for a major part of your application, it definitely belongs in the store. But if you're just toggling a button color when you click the button, you can leave the color state locally-defined. The action of clicking the button might have other effects that require an action dispatch, but that is separate from the simple question of what color it should be.

In general, try to keep your application state as small as possible. You don't have to shove everything in there. Do it when you have to or it makes a lot of sense to keep something there. Or if it makes your life easier when using Dev Tools. But don't overload its importance too much.

For the purpose of highlighting the great link provided by @AR7, and because that link moved a while back:

Use React for ephemeral state that doesn't matter to the app globally and doesn't mutate in complex ways. For example, a toggle in some UI element, a form input state. Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.

Sometimes you'll want to move from Redux state to React state (when storing something in Redux gets awkward) or the other way around (when more components need to have access to some state that used to be local).

The rule of thumb is: do whatever is less awkward.

Dan Abramov: https://github.com/reactjs/redux/issues/1287#issuecomment-175351978

Redux FAQ: Organizing State
this part of redux official doc well answered your question.

Using local component state is fine. As a developer, it is your job to determine what kinds of state make up your application, and where each piece of state should live. Find a balance that works for you, and go with it.

Some common rules of thumb for determining what kind of data should be put into Redux:

  • 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)?

Yes, it's worth striving to store all component state in Redux. If you do, you will benefit from many features of Redux like time travel debugging and replayable bug reports. If you don't, those features could be completely unusable.

Any time you do not store a component state change in Redux, that change is completely lost from the stack of Redux changes and your application UI will be out of sync with the store. If this is not important to you then ask yourself why use Redux at all? Your application will be less complex without it!

For performance reasons, you may wish to fall back to this.setState() for anything that would dispatch many actions repeatedly. For example: storing the state of an input field in Redux each time the user types a key may result in poor performance. You can solve this by treating it like a transaction: once the user action is "committed," save the final state in Redux.

Your original post mentions how the Redux way is a "hell of a lot of code to write." Yes but you can use abstractions for common patterns such as local component state.

This is the problem with Redux, developers overengineer state designs, which always turn applications that could be fairly simply into complex structures.

Redux to me, is a react architecture that should be well planned and designed before implementing.

https://kentcdodds.com/blog/application-state-management-with-react/ ”One of the reasons redux was so successful was the fact that react-redux solved the Prop-Drilling problem. The fact that you could share data across different parts of your tree by simply passing your component into some magical connect function was wonderful. Its use of reducers/action creators/etc. is great too, but I'm convinced that the ubiquity of redux is because it solved the Prop-Drilling pain point for developers.

This is the reason that I only ever used redux on one project: I consistently see developers putting all of their state into redux. Not just global application state, but local state as well. This leads to a lot of problems, not the least of which is that when you're maintaining any state interaction, it involves interacting with reducers, action creators/types, and dispatch calls, which ultimately results in having to open many files and trace through the code in your head to figure out what's happening and what impact it has on the rest of the codebase. “”

Simply said: NO!

React was not created for Redux, and Redux was not created for React.

From the official website (https://redux.js.org/faq/general):

I would like to amend this: don't use Redux until you have problems with vanilla React

So if you can do your job without Redux it is recommended not to use it. And my past experience confirms that it is a very healthy recommendation for multiple reasons:

  1. Lots of boiler plate that will grown along with the application (this applies also for Redux toolkit, you will still have a lot of extra code but not as much as with plain Redux).

  2. You can't prototype something very fast to try and see if it works.

  3. Will slow down delivery, so if yo have to deliver constantly in a fast manner, there is no time for Redux.

  4. Components cannot be reused with different data sets. Think of having 100 typeaheads across your app for searching for 100 different types of entities. You have to adapt all entities to the same data structure plus you have no control on the order of writing to that state. Any instantiated component can dispatch to that state from anywhere and any time.

  5. Global access is usually bad in programming because it can bring the application into an unpredictable state. Every instantiated component can change anything any time. So it is a high probability that you can end up in states you never dreamed of. And this probability grows along with the application because you are growing the global access. Of course Redux has amazing dev tools and architecture that help you track the paths that put the application into an unpredictable state. But why spend time on tracking when you cand avoid it?

  6. Global access is bad for developing new features + bug fixing, because changes in place A can ruin something in places B, C, D because A, B, C and D share the same global state. So when you fix/develop something you have to do regression every where, where the state is used, which again is time consuming and things get worse as the application grows. Fast bug fixing in production will become a nightmare if you do not have bullet prof automated regression tests.

  7. Unit testing becomes way more harder for components that use Redux because you throw away dependency injection when using Redux. And every developer knows that dependency injection is one the best friends of unit testing. Anyway you will have to find a third party library that will do some magic mocking of the Redux part and still you will have issues as the number of unit tests grows, mostly because of the the global access and the magic mocking which is not in your control. Every developer knows that global access is one of the worst enemies of unit testing and extensible code. So unit testing will become a nightmare as the application grows.

  8. As the application starts growing the state well keep growing and it will be very hard to follow especially if not planned right.

  9. As the application grows you will encounter performance issues (UI level).

So based on my experience I would say don't do that and try to identify if you have problems that Redux can solve and vanilla React can't, and only then use it.

I often read online that Redux should be used for large application. And I disagree with this statement for the reasons mentioned above. As longer the global state and global access grow, the higher the probability the development, bug fixing, testing time will grow too, because "global" is usually an evil word in programming for many reasons. So you have to be careful when using "global" stuff. Of course Redux is a special kind of global access with a controlled way of doing things and time traveling. But still it has to be used wisely. Global access in general can be very useful or very harming depending on how, when and for what purpose it is used.

I would say Redux is more fit for complex applications, and even small applications as long as they have a complex part. But for sure not large applications if the complexity is low. An let me give you and example:

You can have a large SPA with 100+ "pages", and the client wants to be delivered yesterday. Each page represents a CRUD interface for a different type of entity, not very complex. No Entity is used in two separate pages. Why would would you add Redux everywhere in this case, when every page has nothing to do with the other? You can keep local state at each page level, incapsulated, inject the data service via default props as an abstract async function and that's it. You will have less code, deliver faster, test faster because you inject the data service in an abstract way and you don't have to worry that you or someone else will break something in pages B, C, D by touching page A.

On the other hand you can have a small application, let's say even one "page". But that page contains 30 distinct inner components, where each component is user interactable and the user can do lots of clicks every where, and double clicks, and right clicks, and drag and drops every where, and resizing and typing, and some components state depend on other component state, some components interchange with other components depending on state and so one. For sure Redux is better choice here.

Or some people say that you should do everything with Redux because you have a single source truth. But in case of server data, isn't the endpoint the single source of truth? Why do you want to centralize a copy of the server data in the UI when you already have a single centralized source of truth: the actual BE endpoints. Querying an endpoint is way more truthy than querying a global copy that can be changed any time from anywhere in the UI. If somebody says but what about caching, well yes, first of all you have to know if you need caching at all and second Redux was not developed for caching. Dedicated libraries exist for caching like React Query which is way more elegant and flexible for this purpose. And as for UI state (ex: isModalVisible), each "page" should have it's own encapsulated single source of truth. Because globally accessing things that are meant to be local is again bad practice for all the reasons mentioned above.

I often heard that you should put server data in Redux because the component should not know about the data source. Sure but if you inject an async function via props that makes the request, the component will still not know the data source. It is using an abstraction and it is easily testable without installing a bunch of other helpers.

Some use Redux to avoid prop drilling but usually if the "page" is not complex you shouldn't end up with a deep tree of components. Most components are most probably layout components and you can use the children property to extract the middle components that you have to drill only to pass further the data somewhere below .

So as a conclusion, I say the same thing as it says on the official website:

"Don't use Redux until you have problems with vanilla React".

Redux was not developed for storing server data, you can do this with vanilla React. Redux was not developed for UI state, you can do this with vanilla React. Redux was not developed for caching, this is another story and it usually has to be done server side, but if on the server there is no possibility for some reason, they are dedicated tools for caching on FE like React Query. Redux was not developed for large applications, you can do this in vanilla React if you are bit smart and organized.

Redux was developed to solve complex problems, for complex applications or applications that contain complex parts, that will give you big problems in vanilla React. Think like Facebook, where the application is huge + complex, and each request counts because they have billions+ each day, everything has to be fixed quick. As far as I read on different blogs, they didn't build the architecture on Redux, they introduced it when they actually encountered problems that had not other solution. To avoid performance issues (each request and opened web socket counts) for such a highly "stressed" app and provide a relatively easy fix, they needed a global access mechanism (so global is the hero here) to fix something not to build on top of it... and they also needed this global access to be predictable and time traveling so they can debug and trace the path that put the application in an undesirable state.

So unless yo have problems with vanilla React, unless you have complex problems to solve, you don't need Redux. You just need to be a bit smart and organized.

Think as React state as the police department and Redux state as the swat team. You call the swat team when the police department doesn't manage. You don't make the swat team do police work just because the police has a lot of work to do (large app). You let the swat team do the things it was trained for.