Question: How to choose between Redux's store and React's state? #1287

Closed
lomotony opened this Issue Jan 27, 2016 · 23 comments

Projects

None yet
@lomotony

No description provided.

@gaearon
Member
gaearon commented Jan 27, 2016

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.

@gaearon gaearon closed this Jan 27, 2016
@gaearon gaearon added the question label Jan 27, 2016
@lionng429

For data fetched via network requests, always use stores in order to support server-side rendering (if you believe that is the ultimate goal), or you won't be able rehydrate.

For those state to be listened by two or more containers, it should also be in stores?

@xogeny
xogeny commented Jan 27, 2016

I agree with @gaeron about the distinction of ephemeral vs. persistent. But I actually think about this in three categories. One is the UI state that he talks about and I also think of as ephemeral. Another is the application state which is really the persistent core of the application. But a third, which sits in between, is routing state. I use the term "routing" because it is familiar to people, but I abstract this to a "view selection" state since I think that spans desktop, web and mobile better.

Now you could argue that this is UI state because it is deciding what people see (much like the state of tabs, for example). But I see two distinctions. The first is that the state is serialized (i.e., as a URL) and sent to other people. So things should go in the route state if you want people to be able to "deep link" right to that particular UI state. The second is that in many cases the initial route state or a change in route triggers a change in application state (i.e., loading the data to be viewed). Of course, actions in the UI do the same thing. But the distinction I make is that you can (and should) have route state without any view or rendering precisely to test the "application logic" around changes in route state. And it is because the view and rendering don't need to be involved that makes it partly application state, IMHO.

How does this relate to the question of Redux vs. React for state management? Application state is the domain of Redux and UI state is the domain of React. But routing state should (in my opinion) be managed by Redux even though it can be viewed as UI state (see the embedded links for more discussion of why I think that).

To be clear, @gaearon's comment about things moving around still applies. But I just find it useful to distinguish these different cases.

@gaearon
Member
gaearon commented Jan 27, 2016

Application state is the domain of Redux and UI state is the domain of React.

Note that I don't claim this. I think it's fine both to keep some app state in React, and some UI state in Redux. I don't think they should be separated by domains.

The way I think about it, if you create an app with Redux, embrace the single state tree. Put UI state there as well. However if it gets tedious and frustrating don’t be afraid to put state into the components. My point is that use single state tree unless it is awkward, and only do this when it simplifies things for you rather than complicates them. That’s the only guideline.

@xogeny
xogeny commented Jan 27, 2016

First, I certainly didn't mean to put words in @gaearon's mouth, sorry for that.

Second, I completely agree with @gaearon. I also believe firmly in a single state tree approach. When I talked about UI state, I had in my mind really minor things (like what is the current tab selected) where it might not really be that relevant to the application state (exactly has @gaearon discussed).

So let me clarify my position. I agree that pretty much everything should be in Redux, including the route state (as I've done in my TodoMVC implementation with Redux and TypeScript. I specifically mentioned route state and distinguished it because I think (and you can see it in that TodoMVC implementation) that it definitely belongs in Redux (and shouldn't be connected at all to rendering) and is most definitely not "UI state".

For React components I rarely use state. I prefer to use props both to get information about what to render and to get closures that I can invoke to trigger changes external to my component. There are some infrequent circumstances where I introduce state in components just to make life easier, but I try to avoid it. I hope that clarifies things.

@inetfuture

I think this question is really subjective and complicated, so I made a tough decision with my team today, don't bother:

  • for non-reusable container, which has connection to Redux, just put everything into Redux store, even tiny UI state like if a modal is open, don't use this.setState any more.
  • for reusable component, which has nothing to do with Redux, use React state.

And now we're implementing some helpers to make it less tedious to manage tiny UI state.

@gaearon @lionng429 @xogeny Any drawbacks you can think of this approach?

@xogeny
xogeny commented Jan 28, 2016

@inetfuture I tend to be a bit miserly about application (redux) state because I'm using TypeScript and I define a type for my application state all the way down. That being said, I've gotten in the happen when developing component libraries to separate all the state manipulation from the rendering (see links in previous comments for more details). This means that even for general components that aren't trivial, I completely externalize the state. I generally pass everything in through props (both states and closures for manipulating state). That way, I can easily hook it into my application state. Part of this is undoubtedly an artifact of using TypeScript and not being able to just use combineReducers and connect to hook things in (and preserve the type constraints at least). So this is probably not a representative viewpoint. But I will say that why you say "just put everything into Redux store", I would worry that you'll end up with a kitchen sink of state. I think the fact that my use of TypeScript means that it takes some effort to expand application state is not necessarily a bad thing because it forces me to decide "do I really need this?" instead of letting stuff just pile up.

@markerikson
Contributor

As an alternate thought: local component state can be useful for controlled inputs that need a fast update time, as well as reducing the number of actual actions triggered. In my current prototype, I put together a form-editing HOC that receives the current item being edited as a prop. It also handles form input change events by saving the changed field in its state, then calling a debounced "EDIT_ITEM_ATTRIBUTE" action creator with the combined local WIP changes. The result is that the form fields update immediately, because only the form itself is being re-rendered, and a lot fewer actions are triggered (like, if I held down 'A' for a few seconds, only the 'AAAAAAAAAAAA' value would be sent as an action rather than 'A', 'AA', etc).

I've got the HOC up as a gist over at https://gist.github.com/markerikson/554cab15d83fd994dfab , if anyone cares.

Anyway, the point is that that component state and store state both have their uses - you just gotta consider your actual use case.

@sompylasar

As for reusable components, if they are large and complex enough, it would be quite future-proof to have their state in Redux, mainly for traceability and replay for testing. But there are still some doubts on how exactly this code reuse should be architected (components plus actions plus reducers).

@galkinrost

In our applications we solved this problem in connect-like style. https://github.com/Babo-Ltd/redux-state

@silvenon

I rarely found it awkward to store states in Redux. I like the power it provides, that down the road I can make it interact with some other component if I need it to.

The only thing that’s coming to my mind where it would be awkward is handling the state of form elements, because there can be so many of them. In that case I use a library like redux-form.

@idavollen
idavollen commented May 5, 2016 edited

My point is that use single state tree unless it is awkward, and only do this when it simplifies things for you rather than complicates them. That’s the only guideline

@gaearon would you like to share your experiences on how to decide when or how it is considered to be awkward? for exmaple, can you give some typical scenarios/examples you think it's awkward enough if they are put in application state atom? thanks in advance!

@silvenon
silvenon commented May 6, 2016 edited

@idavollen I can give a couple:

  • a dropdown open/close state, otherwise you would have to keep track of all the dropdown components in your store
  • when you're debouncing an <input> change, you can use state to update the value instantly (and you're not risking other components to unnecessarily re-render) and update the store in a debounced manner

Basically you should use it for state which doesn't affect other components.

@idavollen

I have a third situation to consider, for instance, I have a form (a react container component that has expensive calculation in render method) consisting of many lines with checkbox preceded and a submit button. When the submit button is clicked, the form should submit the lines whose checkboxes are checked, which means transient states of individual checkbox before submitting form when user toggles it is not important and should not be treated as component state and toggling of checkbox should not cause render() to be called.

Fore the time being, I'd neither put states of checkbox in application state atom, nor in local component state, i.e. the form react component in order to avoid un-necessary calling of render() while toggling checkbox, on the contrary, a instance variable is preferred to hold the checked checkboxes, however it's against react-pattern.

I'm wondering what's the best approach of managing these states of checkboxes in this case?

@silvenon
silvenon commented May 9, 2016 edited

@idavollen are those checkboxes uncontrolled? If not, you are re-rendering them anyway, so you can keep track of which ones are selected in component's state and use that to select which parts of data will be submitted.

@tiberiumaxim

How fast is Redux compared to the components' state? Should I rely on Redux for UI props that are really sensible in terms of timing between the actual event and the rendering process? I have a complicated situation where using components state would require a lot of not so straight forward communication. Any help in this matter would be highly appreciated.

@deowk
deowk commented Oct 19, 2016

@tiberiumaxim In my experience (I develop apps in react and redux that run on low powered devices like Smart TV's). In most cases updating component state will be more performant, just because the diff'ing that needs to happen is on a smaller data set and no dispatches need to happen. In cases where only the component or children need access to that state it would be better to stick with local component state and this is often the case with UI related state, if however you need to persist that data into the redux store at some point - you might need to use a combination of local (component) vs global (redux) state and make sure you persist that data to the redux store at the correct time - i.e when a user navigates away from the page - or something to that effect. Maybe if you could shed some more light on your specific case - I could give you more detailed information

@markerikson
Contributor

It's up to you to decide what state goes in Redux, and what stays local to a component. You may want to read through http://redux.js.org/docs/faq/OrganizingState.html#organizing-state-only-redux-state and http://redux.js.org/docs/faq/Performance.html#performance-scaling for some related information.

@tiberiumaxim

@deowk thanks for sharing your knowledge on the performance matter, I thought the same but needed confirmation.
Also, @markerikson thanks for the links, I'll have a look at them once again.

@sompylasar

Not sure if anyone mentioned that explicitly, but local component state can be managed by Redux as well. You can create a store right in your component constructor. This local store would contain the state of this component, and handle actions related to this component (and its children by passing either the dispatch function of this store, or callbacks that call that dispatch function).

This architecture can be implemented manually, or use some library like redux-fractal, redux-ui and so on.

@markerikson
Contributor

Or, as Dan has pointed out, you can even implement a reducer-style approach to updating a component's state as well! See https://twitter.com/dan_abramov/status/736310245945933824

@borisyankov borisyankov referenced this issue in zulip/zulip-mobile Oct 23, 2016
Merged

Multilogin #59

@akshay2604

I want to know where to store UI state like activity indicator and modal open or close. Does using setState cause problem in unit testing of react components?

@markerikson
Contributor

@akshay2604 : again, that's up to you. See the links I posted in previous comments in this thread. You can definitely use setState while testing components, especially if you're using the Enzyme library to help test them.

@jakeowns jakeowns referenced this issue in jakeowns/react-live-graph Jan 10, 2017
Closed

Violating Redux Principles #1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment