Redux: What are the disadvantages of storing all your state in a single immutable atom?

Created on 10 Feb 2016  ·  91Comments  ·  Source: reduxjs/redux

I understand that this is the principle underlying all of redux, and that it comes with all these awesome benefits that are pretty well known by now.

However, I feel that one place that redux is lacking in, is that it doesn't openly describe the conceptual disadvantages of using this architecture. Perhaps this is a good choice since you want people to not shy away due to the negatives.

I'm just curious because it applies not just to redux, but in some capacity to other things like Om, datomic and that talk about turning the database inside out. I like redux, I like Om, I'm interested in datomic, and I really liked that talk.

But for all the searching I've done, it's hard to find people critical of the single immutable store. This is an example, but it just seems to have a problem more with the verbosity of redux than with the actual architecture.

The only things I can think of is that perhaps it takes more memory to keep storing copies of your state, or that it's a little harder to rapidly protoype with redux.

Because you guys have probably put a lot more thought into this than I have, could you help elucidate this for me?

discussion docs question

Most helpful comment

Redux is basically event-sourcing where there is a single projection to consume.

In distributed systems, there is generally one event log (like Kafka), and multiple consumers that can project/reduce this log into multiple databases/stores that are hosted on different servers (typically, a DB replica is actually a reducer). So in a distributed world the load and memory usage is... distributed, while in Redux if you have hundreds of widgets that all have their local state, all this run in a single browser, and every state change has some overhead due to immutable data updates.

In most cases this overhead is not a big deal, however when mounting text inputs into a not so good state shape, and typing fast on a slow mobile device, it's not always performant enough.

Rendering that state from the very top is, in my experience, is not convenient and also not always performant enough (at least with React which is probably not the fastest VDom implementation). Redux solves this with connect but still, as the number of connection grows, it can become a bottleneck. There are solutions

Also persistent data structures like ImmutableJS are not always offering the best performances on some operations, like adding an item at a random index in a big list (see my experience rendering big lists here)

Redux includes both the event log and the projection because it is convenient and is ok for most usecases, but it could be possible to maintain an event-log outside redux, and project it into 2 or more redux stores, add all these redux stores to react context under different keys, and have less overhead by specifying which store we want to connect to. This is absolutly possible, however this would make api and devtools harder to implement as now you need a clear distinction bettween the store and the event log.


I also agree with @jquense

The benefits of easy hydration, snapshots, time travel, etc only work if there is no other place important state lives. In the context of React this means you need to store state, that properly belongs to components in the Store, or lose a bunch of benefits. If you do want to put everything in Redux it often ends up being burdensome and verbose, and adds an annoy level of indirection.

Mounting any state to Redux store requires more boilerplate. Elm architecture probably solves this in a more elegant way but also requires a lot of boilerplate.

But it is also not possible or performant to make all state controlled. Sometimes we use existing libraries for which it is hard to build a declarative interface. Some state is also hard to mount to redux store in an efficient way, including:

  • Text inputs
  • Scroll position
  • Viewport size
  • Mouse position
  • Caret position / selection
  • Canvas dataUrls
  • Unserializable state
  • ...

All 91 comments

That’s a great question. I’ll try to write a comprehensive answer tomorrow but I’d like to hear from some of our users first. It would be nice to summarize this discussion as an official doc page later. We didn’t do this at first because we did not know the drawbacks.

Thank you very much! I've really been wanting to hear about this.

By the way, I encourage everyone who wants to contribute to this discussion to also give an idea of what they were building with Redux so it is easier to understand the scope of the projects.

Its sort of a tough question b/c projects like OM and redux and other single state atom libs, actively mitigate the downsides. Without the the immutability constraint, and gated, controlled access a single state atom is not at all unlike attaching all your data to the window (of which the downsides are well known)

Specific to the Redux approach though, the biggest downside for us is that single state atoms is sort of an all or nothing thing. The benefits of easy hydration, snapshots, time travel, etc only work if there is _no_ other place important state lives. In the context of React this means you need to store state, that properly belongs to components in the Store, or lose a bunch of benefits. If you do want to put everything in Redux it often ends up being burdensome and verbose, and adds an annoy level of indirection.

None of these downsides have been particularly prohibitive for us though :)

So, I've been eyeballing Redux, and this style of architecture, to model game states; I have an interest in making Civ style simulated worlds, and fully recorded board game histories more simply, in a browser context, which is perhaps an unusual use case to some, but I doubt anyone would want me to stop, either.

In that context, I'm interested in efforts to manage the size of the history data structure, to move parts of it to the server or disk to save it, restoring specific segments, etc.

The thing I've struggled with, as a new user the most is trying to overcome both the extreme (imho) coding style change in Redux, and the conceptual changes at the same time; it remains a bit daunting even now, a month and change after trying to deep dive into it; I'm looking into https://github.com/ngrx/store next to get at the concepts but with a more familiar style/syntax and more of the rest of the framework implied/provided.

I understand that for some, a framework is a crutch; but some of us need those; few people will be at the top of their game enough to have useful strong opinions, so a frameworks' are better than fumbling in the dark, you know? So that's a point from me, a mid level, middle aged programmer new to the concepts :)

Basically, Redux and your videos taught me how to do it in raw javascript, but being a less experienced programmer, I still have no idea how to deliver a product without the further guidance, so until I see that example of a finished thing, I just kinda stand around like:

Not your fault, but still a problem I'd love to help solve :)

I think the biggest question I still have at this point is, where should any non-serializable items like functions, instances, or promises go? Was wondering about this over in Reactiflux the other night and didn't get any good responses. Also just saw someone post http://stackoverflow.com/questions/35325195/should-i-store-function-references-in-redux-store .

Use case for multiple stores (keep in mind, this is the best one I could think of):

An app that combines multiple sub-apps together. Think of something like the My Yahoo or another customizable home page product. Each widget will need to maintain its own state without overlap between the two. A product team is likely assigned to each widget, so they might not know the effects of others' code on the environment.

Gathered, you can likely achieve this in Redux by violating a few rules and being careful about propagating those stores on something like React's context. But it might be easier with a framework that handles multiple state atoms by default.

@gaearon we are building trading platform. I've already explained to you once why one store doesn't fit for us (after React.js meetup in Saint-Petersburg). I'll try to explain it again in details (in blog post on medium?) but I probably need help due to my English skills :) Is it ok if I'll send it to you or someone else here for review in Twitter direct messages when it will be done? I can't name the exact date though but I'll try to write this post soon-ish (most likely end of this month).

And yes we are still using Redux with multiple stores by violating some rules from docs as @timdorr said (in cost of that we can't use react-redux as comfortably in cases were we need data from various stores as it is in case of single store)

Redux is basically event-sourcing where there is a single projection to consume.

In distributed systems, there is generally one event log (like Kafka), and multiple consumers that can project/reduce this log into multiple databases/stores that are hosted on different servers (typically, a DB replica is actually a reducer). So in a distributed world the load and memory usage is... distributed, while in Redux if you have hundreds of widgets that all have their local state, all this run in a single browser, and every state change has some overhead due to immutable data updates.

In most cases this overhead is not a big deal, however when mounting text inputs into a not so good state shape, and typing fast on a slow mobile device, it's not always performant enough.

Rendering that state from the very top is, in my experience, is not convenient and also not always performant enough (at least with React which is probably not the fastest VDom implementation). Redux solves this with connect but still, as the number of connection grows, it can become a bottleneck. There are solutions

Also persistent data structures like ImmutableJS are not always offering the best performances on some operations, like adding an item at a random index in a big list (see my experience rendering big lists here)

Redux includes both the event log and the projection because it is convenient and is ok for most usecases, but it could be possible to maintain an event-log outside redux, and project it into 2 or more redux stores, add all these redux stores to react context under different keys, and have less overhead by specifying which store we want to connect to. This is absolutly possible, however this would make api and devtools harder to implement as now you need a clear distinction bettween the store and the event log.


I also agree with @jquense

The benefits of easy hydration, snapshots, time travel, etc only work if there is no other place important state lives. In the context of React this means you need to store state, that properly belongs to components in the Store, or lose a bunch of benefits. If you do want to put everything in Redux it often ends up being burdensome and verbose, and adds an annoy level of indirection.

Mounting any state to Redux store requires more boilerplate. Elm architecture probably solves this in a more elegant way but also requires a lot of boilerplate.

But it is also not possible or performant to make all state controlled. Sometimes we use existing libraries for which it is hard to build a declarative interface. Some state is also hard to mount to redux store in an efficient way, including:

  • Text inputs
  • Scroll position
  • Viewport size
  • Mouse position
  • Caret position / selection
  • Canvas dataUrls
  • Unserializable state
  • ...

This question I just saw on SO:

http://stackoverflow.com/questions/35328056/react-redux-should-all-component-states-be-kept-in-redux-store

Echos some confusion I've seen about managing UI state, and whether the UI state should belong to the component or go into the store. #1287 offers a good answer to this question, but it's not so initially apparent and can be up for debate.

Additionally, this could be a hindrance if you're trying to implement something like this https://github.com/ericelliott/react-pure-component-starter in which every component is pure and does not have a say in its own state.

I found it difficult to use redux's single state tree when I needed to manage multiple sessions' data. I had an arbitrary number of branches with an identical shape and each had multiple unique identifiers (depending on where the data came from, you'd use a different key). The simplicity of the reducer functions and reselect functions quickly disappeared when faced with those requirements - having to write custom getters / setters to target a specific branch felt overly complex and flat in an otherwise simple and compossible environment. I don't know if multiple stores is the best option to address that requirement, but some tooling around managing sessions (or any other identically-shaped data) into a single state tree would be nice.

Increased probability of state keys collisions between reducers.
Data mutations and declarations are far from the code where the data is used (when we write a code we try to place variable declarations near the place where we use them)

Let me preface with my particular use-case: I'm using Redux with virtual-dom, where all of my UI components are purely functional and there's no way to have local state for various components.

With that said, definitely the hardest part is tying in animation state. The following examples are with animation state in mind, but much of this can be generalized to UI state.

Some reasons why animation state is awkward for Redux:

  • Animation data changes frequently
  • Animation-specific actions pollute the history of actions in your app, making it hard to roll-back actions in a meaningful way
  • Redux state tree starts to mirror component tree if there's many component-specific animations
  • Even basic animation state like animationStarted or animationStopped starts to couple state to your UI.

On the other hand, there are definitely challenges to building the animation state entirely outside of the Redux state tree.

If you try to do animations yourself by manipulating the DOM, you have to watch out for stuff like this:

  • virtual-dom diffs will not take into account your animation manipulation, such as custom styles you set on the node—if you set some styles on a DOM node, you have to remove them yourself if you want them to go away
  • If you perform animations on the document itself, you have to be painfully aware of virtual-dom updates—You may start an animation on one DOM node, only to find that the content of that DOM node was changed mid-animation
  • If you perform animations on the document itself, you have to be wary of virtual-dom overwriting styles and attributes you set on your node, and you have to be wary of overwriting styles and attributes set by virtual-dom

And if you try to let virtual-dom take care of all the DOM manipulation (which you should!) but without keeping animation state in Redux, you end up with these tough problems like these:

  • How do you expose animation state to your components? Local state in your components? Some other global state?
  • Normally your state logic is in Redux reducers, but now you have to add a lot of rendering logic directly into your components for how they will animate in response to your state. This can be quite challenging and verbose.

There are currently awesome projects like react-motion that make great strides forward in solving this problem _for React_, but Redux is not exclusive to React. Nor should it be, I feel—a lot of folks are bringing their own view layer and trying to integrate with Redux.

For anybody who's curious, the best solution I've found for Redux + virtual-dom is to keep two state atoms: Redux keeps the core application state, holds the core logic to manipulate that state in response to actions, etc. The other state is a mutable object that holds animation state (I use mobservable). The trick then is to subscribe to both Redux state changes _and_ animation state changes, and to render the UI as a function of both:

/* Patch h for jsx/vdom to convert <App /> to App() */
import h from './h'
import { diff, patch, create } from 'virtual-dom'
import { createStore } from 'redux'
import { observable, autorun } from 'mobservable'
import TWEEN from 'tween.js'
import rootReducer from './reducers'
import { addCard } from './actions'
import App from './containers/App'

// Redux state
const store = createStore()

// Create vdom tree
let tree = render(store.getState())
let rootNode = create(tree)
document.body.appendChild(rootNode)

// Animation observable
let animationState = observable({
  opacity: 0
})

// Update document when Redux state 
store.subscribe(function () {
  // ... anything you need to do in response to Redux state changes
  update()
})

// Update document when animation state changes
autorun(update)

// Perform document update with current state
function update () {
  const state = store.getState()
  let newTree = render(state, animationState)
  let patches = diff(tree, newTree)
  rootNode = patch(rootNode, patches)
  tree = newTree
}

// UI is a function of current state (and animation!)
function render (state, animation = {}) {
  return (
    <App {...state} animation={animationState} />
  )
}

// Do some animations
function animationLoop (time) {
  window.requestAnimationFrame(animationLoop)
  TWEEN.update(time)
}
animationLoop()

new TWEEN.Tween(animationState)
      .to({ opacity: 100 }, 300)
      .start()

// Or when you dispatch an action, also kick off some animation changes...
store.dispatch(addCard())
/* etc... */

Thank you all for great responses! Keep them coming.

One thing to note is that we don’t intend Redux to be used for _all_ state. Just whatever seems significant to the app. I would argue inputs and animation state should be handled by React (or another ephemeral state abstraction). Redux works better for things like fetched data and locally modified models.

@taggartbg

I found it difficult to use redux's single state tree when I needed to manage multiple sessions' data. I had an arbitrary number of branches with an identical shape and each had multiple unique identifiers (depending on where the data came from, you'd use a different key). The simplicity of the reducer functions and reselect functions quickly disappeared when faced with those requirements - having to write custom getters / setters to target a specific branch felt overly complex and flat in an otherwise simple and compossible environment.

Would you mind creating an issue describing your use case in more detail? It might be that there is a simple way to organize the state shape differently that you are missing. In general multiple branches with same state shape but managed by different reducers is an anti-pattern.

@istarkov

Increased probability of state keys collisions between reducers.

Would you mind explaining how this happens in more detail? Normally we suggest you to only run a single reducer on any state slice. How can collisions happen? Are you doing multiple passes over the state? If so, why?

@gaearon @istarkov : perhaps what's meant is that various plugins and related libraries might be jostling for the same top-level namespace? Library A wants a top key named "myState", but so does Library B, etc.

Yeah, this is a good point even if this is not what @istarkov meant. (I don’t know.) In general libraries should offer a way to mount reducer anywhere in the tree but I know that some libraries don’t allow that.

@gaearon

Would you mind creating an issue describing your use case in more detail? It might be that there is a simple way to organize the state shape differently that you are missing. In general multiple branches with same state shape but managed by different reducers is an anti-pattern.

I'd be happy to! I'll do that when I get a moment.

I think its definitely regarded as an antipattern, although I have a single, shared, reducer to manage those branches. Nonetheless, I feel like the paradigm that redux provides is not far from being a perfect use case for serializing / deserializing identical branches as immutable state. I don't see why it would be against the ethos of redux to build something like reselect with some assumed logic for targeting specific branches by some key.

I'd be happy to chat about it off-thread, I could very well be wrong.

@taggartbg : speaking entirely without knowing what your code looks like here. Are you talking about trying to deal with state that looks like this?

{ groupedData : { first : {a : 1, b : 2}, second : {a : 3, b : 4}, third : {a : 5, b, 6} }

Seems like you could deal with that on the reducer side by having a single reducer function that takes the per-group ID key as part of each action. In fact, have you looked at something like https://github.com/erikras/multireducer, https://github.com/lapanoid/redux-delegator, https://github.com/reducks/redux-multiplex, or https://github.com/dexbol/redux-register?

Also, on the reselect side, the fact that React-Redux now supports per-component selectors might help, since that improves scenarios where you're doing selection based on component props.

@gaearon

One thing to note is that we don’t intend Redux to be used for all state. Just whatever seems significant to the app. I would argue inputs and animation state should be handled by React (or another ephemeral state abstraction). Redux works better for things like fetched data and locally modified models.

A React developer reading the docs and tutorial already has this ephemeral state abstraction at their disposal, so it's possible this is already in mind when considering where Redux makes sense for a project.

However, from the perspective of a developer with little or no experience with React that wants to take a functional approach to UI, it may not be obvious which state belongs in Redux. I've done several projects now where at first Redux and virtual-dom were sufficient, but as the application grew in complexity this other "ephemeral state abstraction" became necessary. This isn't always apparent the first time you add in an animation reducer with some basic animation flags, but later on becomes quite a pain.

It might be nice for the docs to mention which state is right for Redux and which state is better solved by other tooling. It may be redundant for React developers, but could be very beneficial for other devs to have in mind when looking at Redux and planning their application architecture.

EDIT: the title of the issue is "What are the disadvantages of storing all your state in a single immutable atom?" This "all your state in a single immutable atom" is exactly the kind of wording that ends up getting a lot of the wrong kind of state in the Redux tree. The documentation could provide some explicit examples to help developers avoid this kind of trap.

@gaearon We had an interesting conversation over on Cycle.js about the architectural differences between a single atom state vs pipping. HERE.

I know this isn't 100% Redux related but wanted to give a different perspective from an implementation that uses Observable streams as a data-flow around an app (for Cycle the app being a set of pure functions).

Because everything in Cycle is an Observable, I was having difficulties getting state to move from one route change to another. This was due to the state Observable not having a subscriber once a route was changed and thought why not implement a single atom state, so any time state changed in my app it would report back to the top level store, bypassing any piping (view terrible drawings here).

With Cycle because side effects happen in Drivers you usually have to make that loop from top level driver to the component and back, so I wanted to just skip that and go straight back up and out to the components listening. Was taking Redux as a source of inspiration.

It's not a case that either is right or wrong but now I do piping and we have figured out a state driver, having the ability to pipe my state to different components as I need to is really flexible for refactoring, prototyping, and having strong understanding each source of state, each consumer and how they got to each other.

Also newcomers to the application (if they understand Cycle of course), can easily connect the dots and very quickly draw a visual representation in there mind of how state is handled and piped and all this from the code.

Sorry in advance if this is totally out of context, wanted to demonstrate how a different paradigm had a similar conversation :smiley:

@timdorr

Each widget will need to maintain its own state without overlap between the two. A product team is likely assigned to each widget, so they might not know the effects of others' code on the environment.

I think it's better when widget has it's own state (maybe even it's own (redux?) store) so it can work standalone in any (mashup-)app and receive only some of properties it. Think about weather widget. It can fetch and show data by itself and receive only properties like city, height and width. Another example is Twitter widget.

Great discussion!

Mainly, I find it inconvenient to combine multiple apps/components/plugins.

I can't just take a module I build and throw it in another module/app, as I'll need to separately import its reducers into the store of the app, and I'll have to put it under a specific key that my module knows about. This also limits me from duplicating the module if I use @connect, because it connects to the entire state.

For example:

I'm building a messenger that looks like iMessage. It has a redux state of currentConversationId etc. My Messenger component has @connect(state => ({ currentConversationId: state.messenger.currentConversationId })).

I want to include this Messenger in a new app. I'll have to import { rootReducer as messengerRootReducer } from 'my-messenger' and add it to combineReducers({ messenger: messengerRootReducer }) for it to work.

Now, if I want to have two <Messenger> instances in the app that have different data, I can't, because I'm bound to state.messenger in the Messenger component. Making work against a specific slice of the store will make me use a customized @connect.

Also, let's say the containing app has a certain action name that exists on the my-messenger module, there's gonna be a collision. This is why most redux plugins have prefixes like @@router/UPDATE_LOCATION.

A few random/crazy ideas:

1

@connect can connect to a specific slice of the store, so I can include in my app multiple <Messenger>s that connect to their own slice of data, such as state.messenger[0]/state.messenger[1]. It should be transparent to <Messenger> which still keeps connecting to state.messenger, however, the containing app can provide the slice by something like:

@connect(state => ({ messengers: state.messengers }))
class App extends Component {
  render() {
    return (
      <div>
        {this.props.messengers.map(messenger =>
          <ProvideSlice slice={{messenger: messenger}}><Messenger /></ProvideSlice>
        }
      </div>
    )
  }
}

There's a problem when using global normalized entities, state.entities will also need to be present, so along with the sliced state, the entire state needs to be present somehow. ProvideSlice can set some context that Messenger's @connect will be able to read from.

Another issue is how to bind actions fired by components in <Messenger> affect only that slice. Maybe have @connect under ProvideSlice run mapDispatchToProps actions only on that slice of the state.

2

Combine store and reducer definitions, so each reducer can be also standalone. Store sounds like a controller while a reducer is a view. If we take react's approach, "everything is a component" (unlike angular 1.x's controller/directive), it may allow components and their state/actions truly be standalone. For things like normalized entities (i.e 'global state') something like React's context can be used, and it can be accessible through all actions (e.g. getState in thunk)/connected components.

@elado The most clever idea I've seen for now is to architect with "providers" in mind: https://medium.com/@timbur/react-automatic-redux-providers-and-replicators-c4e35a39f1

Thx @sompylasar. I'm on a bit of a road trip atm but when I get a chance I'm planning on writing another article describing providers more succinctly for those already familiar with React and Redux. Anyone already familiar should find it to be crazy simple/easy. :)

@gaearon would still like to hear your thoughts on this.

My biggest pet peeve is it is harder to compose, reuse, nest, and generally move around container components because there are two independent hierarchies at the same time (views and reducers). It is also not entirely clear how to write reusable components that either use Redux as implementation detail or want to provide Redux-friendly interface. (There are different approaches.) I’m also not impressed about every action having to go “all the way” upwards instead of short-circuiting somewhere. In other words, I would like to see something like React local state model but backed by reducers, and I would like it to be highly practical and tuned to real use cases rather than a beautiful abstraction.

@gaearon any ideas on how to begin implementing such a system?

@gaearon

In other words, I would like to see something like React local state model but backed by reducers

I think the tricky part is that React local state is tied to the lifecycle of a component, so if you try to model local state in Redux, you have account for "mounting" and "unmounting." Elm doesn't have this problem because 1) components don't have lifecycles, and 2) the "mounting" of a view is derived from the model, whereas in React, local state only exists _after_ a view has been mounted. (edit: more accurate to say right before it's mounted, but after the decision to mount has already been made)

But as you allude to, the Elm Architecture — going "all the way" upwards — has its own drawbacks and doesn't fit all that well with React's update and reconciliation cycle (e.g. it requires liberal use of shouldComponentUpdate() optimizations... which break if you naively "forward" a dispatch method inside render().)

At a certain point, I feel like just using reducers + setState and calling it a day :D Unless/until React figures out a story for externalizing the state tree... though maybe that's what we're really discussing here

I guess this is what @threepointone is trying to solve with https://github.com/threepointone/redux-react-local

@acdlite the conceptual model of Elm works great for local state but it really looks like a pain to use in real world applications where we have to use libraries, give focus on mount, handle parallax effects or whatever... See https://github.com/evancz/elm-architecture-tutorial/issues/49

@slorber Yeah I agree. Isn't that essentially what I just said? :D

I suppose your concerns are slightly different. _If_ (big "if") you were to use the Elm Architecture in React, though, I don't think you'd have the problems you mention because we'd still have access to lifecycles, setState as an escape hatch, etc.

Yes we are on the same page @acdlite
Elm architecture with React would solve this problem.
Even Elm might in the future as it could allow the use of vdom hooks.

However the Elm architecture requires quite some boilerplate. I think it's not really sustainable unless you use Flow or Typescript for the actions wrapping/unwrapping, and it would be better to have a more simple way to handle this local state, like the solution of @threepointone

I have another concern with the Elm architecture is that nested action works great for local component state, but I think it's not such a good idea once you need communication between decoupled components. If widget1 has to unwrap and introspect deeply nested actions to find some event fired by widget2, there is a coupling problem. I've expressed some thoughts here and think using Elm with 2 "mailboxes" could do the job. This is also what redux-saga can help for in non-elm architectures with flat events.

I start to have a good idea on how to architect scalable redux apps and will try to write something about it when i'm less busy

@gaearon Are you talking about react-redux-provide in your last post here? I'm asking because if you are, based on your response, it's clear you haven't taken a close enough look at it.

Also, regarding @acdlite's mention of externalizing the state tree, this is exactly the problem providers are designed to solve.

The API provided by the provide decorator is certainly an abstraction and yes, it is currently dependent on redux, but thinking of it as being dependent on redux is the wrong way to think about it, since it's really just some "glue" between components and the state tree, as React's context is not yet fully developed. Think of it as an ideal method of manipulating and representing state tree(s) via actions and reducers (i.e., the store's state) as props, where you're able to essentially declare the actions and reducers as propTypes (and/or contextTypes in the future) similar to how you import anything else within your app. When you do this, everything becomes insanely easy to reason about. React's context could potentially evolve to include these basic features, and in which case, it would take an entire 2 seconds to grep and remove @provide and import provide from 'react-redux-provide' so that you're then left with simple components that no longer depend on it. And if that never happens, no big deal because everything would continue to work perfectly and predictably.

I think you're oversimplifying the problem. You still need to solve mounting and unmounting.

@acdlite Got an example?

If one of the things you're referring to is (for example) retrieving props from some database based on some id or whatever, this (among nearly any other practical application you can think of) can be easily achieved with providers. After I've finished some other things I'm working on, I'd be glad to show you how easy it is. :)

May be interesting: reactive state implementations compared.
Elm, Redux, CycleJS... (work in progress)

Another possible need for multiple store is when multiple _time lines_, _time travel_, saving locations and saving frequency are possible and needed. For example, user data and user actions that are specific to a single user and common data and group actions specific to a group of users (e.g. shared multi-user project or document). This makes it easier to separate what can be undone (changes to personal data) from what is fixed (edits already augmented by other users) and also facilitates having different saving and/or synchronization cycles.

We are in the design/early dev phase of an angular 1.x application with angular 2.0 development mind set and thinking to use redux. In the past, We have been using flux with multiple stores responding to some common dispatches and to some dedicated dispatches.

Redux looks great to me, but I am not able to reason the single store concept for the entire application. As others also have pointed out above, we have a number of big widgets which are supposed to be hosted on different consumer applications and needed dedicated data stores for each of these.

Even though reducers can be split and can live with the feature/widget that uses it, we have to club all of them when it's time to create the store. We would like to see our widgets exist without having any direct/indirect reference to reducers that it don't care about.

We are really looking forward for some guidance in form of documentation or a comment to outline the best practice in such situations prior to start using redux in our app. Any help will be really appreciated. Thanks.

@VivekPMenon : what are your specific concerns? Size of the state tree? Being able to namespace actions / keep reducers isolated? Ability to dynamically add or remove reducers?

For what it's worth, I just completed a links list cataloging the Redux addon ecosystem. There's a number of libraries that try to address some of those isolation / duplication / scoping concepts. You might want to take a look at the reducer page and the local/component state page to see if any of those libraries might help with your use cases.

@markerikson. Thanks much for the reply. My confusion is mostly around the 2nd point (Isolation). Assume one of our teams creates widget A that is flux based and need to use store for managing its state. Assume they will create a jspm/npm package out if and distribute it. Same is the case with another team that creates widget B. These are independent widgets/packages and don't need to have direct/indirect dependency to each other.

In this context, in regular flux world. Widget A and B have their own stores within their package(They may listen to some common actions, but assume they have more actions that are not common). And both widgets will have dependency on the global dispatcher. In the redux pattern, where would that store be created (physically). Both widget A and B UI component needs to use store instance for listening to changes and to get state. In regular flux, they operate on their own store instance. I am trying to visualize how single store concept is going to play with this situation.

@VivekPMenon this is a valid question that some of us are trying to solve.

You can get some insights here: https://github.com/slorber/scalable-frontend-with-elm-or-redux

Also, take a look at my anwser about Redux-saga here: http://stackoverflow.com/a/34623840/82609

@slorber Thanks much for the guidance. Looking forward to the ideas coming up over here https://github.com/slorber/scalable-frontend-with-elm-or-redux

@taggartbg Did you eventually opened an issue? I am interested in a similar thing - when having a react-router with different independent sub-trees, I find it problematic to keep previous route's state while navigating to a new branch.

@slorber

Let me begin by saying that redux saga is a wonderful project with very much though put into it and this by no means tries to diminish that.

But I wouldn't use it in any of my projects at it's current state. Here are a few reasons why

Sagas are stateless

You can't serialize the state of a generator, they make sense while they're running but they are bound completely to the state of the saga. This causes a series of limitations:

  • You can't time travel generators
  • You can't trace the path that it led to the current state of a saga
  • You can't define an initial state
  • You can't separate the business logic from the application's state [*]
  • You can't store sagas

[*] This could be interpreted as defining business logic within the definition of the application's model, making the M and the C in MVC the same thing, which could be considered as an antipattern

Dispatching actions is non-deterministic

One of the big advantages of using redux is keeping a simple understanding of how the state reacts to an action. But redux-saga make actions prone to unexpected results since you can't tell in which part of the saga you're starting from.

Serializable state

The thing is that the current state of the saga has an impact on the current state of the application, and people stress about keeping the state serializable, but how do you translate that to sagas? If you can't fully serialize the state then you're not _really_ serializing it. You basically can't recover the state of an application without recovering the saga as well.


Storing logic in the state is bad, and in my opinion this is what redux saga does in a way.

Its perfectly OK to assume that the state of your application is specific to _your_ application. That means that it is also OK to assume that the application's state is predictable as long as it operates on top of _your_ application. This means its OK to keep information of the store that determines how your application behaves without actually saving the logic there.

@eloytoro

I understand your concerns. If you look at redux-saga issues you will see that these things are discussed and there are solutions to be found.

Also note that redux-saga choose to implement with generators, but it's not at all a requirement to implement the saga pattern, and there are pros and cons.

There is a select effect which permits you to use redux state inside your redux-saga, so if you don't want to hide state inside generators, you can simply put your saga state inside redux-store directly, but it involves more work.

There are saga devtools that permit to trace the execution of sagas.

There are possible solutions for time-travel but no concrete implementation yet

Right. Quite a long topic and very interesting. I've been comparing different state-management approaches.

I'm keeping a few things in mind while comparing:

  1. single source of truth
  2. how easy/hard it is to render on server-side and feed initial data on client-side
  3. devtools like undo/redo and persistant history storage after reloads.
  4. animations, animations, animations. I always ask myself _"how would animations work with solution a or b"_. This is a key for me as the products I'm developing are UX-centered. I share much of the confusions @jsonnull has.
  5. reuse of components inside another app
  6. hydration of referenced data (_major pain_) in order to keep integrity (related to feeding initialdata from server-side or any other data-source)
  7. community of other devs for discussion like this one

So these are points I'm wary of when thinking about state-management. Redux is the winner by ticking most of the boxes. I know Redux pretty well and it was the first things that got me breathing calmly in the realm of client-side js.

The most notable other projects I've been looking into are the SAM pattern (_hard to get into the mindset and no tools_) and Mobx (_known for its mutable storage_).

Both share the idea of single source of truth and I think it's interesting to see how they handle it.

My (limited of course) research about Mobx is resulting into this:

  1. Mobx (just as Redux) is advocating for single source of truth but as a class with observable properties. This concept creates the differences with redux and flux as a whole:

    1. Mobx also advocates for sub-stores (_nested/part of the global single one_). To me it looks a lot like definig a client-side schema. It's a bit annoying as you probably have schema on the server as well. I see similarities with redux-orm. AFAIK the sub-stores are intended to be passed to components as props (this way the component is not relying on mapping state to props or certain key of the global tree). However passing a single property breaks my idea of "properties" - they should be descriptive of what the component needs. This actually makes me think if that would be a problem if React propTypes can be used to define the shape of the store being passed. Also: having store and sub-stores as classes make it possible to reference the data on directly without need of normalization, but that's kind of pointless if you want to feed intial data because of:

    2. The idea of store as class however means the developer don't have serialization out of the box, which means that's your job to do if you want to feed initial data.

  2. History and undo/redo can be achieved via taking snapshots of the state after it's changed. But you need to write serializer, deserializer yourself. Example is given here: https://github.com/mobxjs/mobx-reactive2015-demo/blob/master/src/stores/time.js
  3. The good thing about writing serializers is that you can leave stuff out. it looks like a good place to store non-persistent animation-related data. So if you serialize it, you just ignore it and you don't have a redo/undo full of animation steps.
  4. Mobx advocates for keeping derivations of data in the store (as methods of the store) opposed to reselect+ connect decorator. This seems kind of wrong to me.
  5. Due to its observable nature, mobx makes easy to define efficient pure components. Shallow comparison of properties is not a problem because the contents is observable anyway.

Comparing SAM and Redux, I like to think about how the logic flows:

  1. Redux:

    1. Define a store

    2. Define functions that handle store changes (reducers)

    3. Define event (redux action) identifiers (redux action type) for every store change and run specific reducer when it matches

    4. Map store to component container properties, optionally passing handlers of user interaction (click, type, sscroll, etc) as functions that simply trigger event/dispatches action.

    5. Component triggers an event (redux action) which is matched against all reducers

    6. Rerender with the new state.

  2. SAM:

    1. Define a store.

    2. Define a accessor/setter function for the store. This setter accepts any data and purely based on the data it tries to figure out where to put it in the store and whether it needs to request data from server or persist it there. Figuring out what to do with the data is not always possible by just inspecting it, so sometimes identifiers/flags are used (like isForgottenPassword). This setter is also intended to validate the data and store the errors instead. After setter is finished -> rerender with the new state.

    3. Define functions that accept piece of data, transform it (alternative to redux reducers) and execute the setter passing the transformed data. Those functions has now idea about the shape of the store. They only know the data they've been passed.

    4. Map store data to component container properties but also it's obligatory to pass handlers of user interaction handlers (click, type, scroll, etc). Note these are the actual functions (known as reducers in redux) and not functions that execute a dispatch call.

    5. Note: there are no dispatching of actions, straight passing of reducers.

So, all of redux, SAM and Mobx keep the state in one place. The way they do it, brings their own setbacks. None can beat redux and its ecosystem so far.

The biggest downside I see is also the handling of non-serializable state.
In my case I have a plugin API and therefore actions and logic that has nothing todo with Redux but needs to interact with my application. So basically I need to initially recreate a bunch of classes based on a given state.

I'm working on some concepts how to handle this kind of non serializable state in a better way. Including re-creating it from any given state. I already managed to move everything but function/object references back into the store. Whats left are objects with their own lifecycles with a defined API that interact with my application and therefor with my actions. - Feels similar to React components, just not for UI rendering but custom logic.

I don't know if you guys have read this article from Erik Meijer. Just a simple quote:

Contrary to popular belief, making state variables immutable comes nowhere close to eliminating unacceptable implicit imperative effects

Perhaps it is time to revisit all this immutability nonsense. Everyone will tell you that's a performance hog when the single state tree grows in size. The SAM pattern makes mutation a primary class citizen of the programming model, lots of simplifications happen when you do that from not needing Sagas any longer, to all these ancillary state machines required to keep track of effects (e.g. fetching).

The article doesn't really argue in your favor. Erik Meijer does revisit "this immutability nonsense", and embraces it completely. He argues that removing state mutation is a basic first step, fundamental but not sufficient. His ultimate goal is the complete elimination of implicit side effects, which would make any state management obsolete(there would be no state to manage!).

I'm a fan of Erik Meijer's work and will eagerly read and listen to anything that he has to say, while also keeping in mind that he does not operate under the same business, technological and intellectual constraints as me.

the key problem in software engineering is that everyone seems to believe that an assignment IS-A mutation. Programming languages only support assignments, mutation is left to the interpretation of the programmer.

Adding a function wrapper to a bunch of assignments is not a going to automagically become the best way to mutate state. That is nonsense.

There is a theory behind (Application) State Mutation, it's called TLA+, strangely enough... Erik never mentions TLA+...

JJ-

Let me elaborate since there seem to be a lot of confusion. Erik's paper demonstrate eloquently that imperative programming languages are somewhat broken when it comes to mutating state. I believe everyone agrees on that.

The question is how do you go about and fix it? Wouldn't you agree that this is the kind of problem that requires a "first principle" approach?

There is pretty much a single principle that we can all agree on in computer science, Dr. Lamport states it as: "Much of computer science is about state machines." and he continues: "Yet computer scientists are so focused on the languages used to describe computation that they are largely unaware that those languages are all describing state machines."

Indeed all programming languages enables us to specify "state machines" regardless of their semantics. Unfortunately a lot of programming languages are biased towards "action" rather than "state-action". That is for good reason, there is a large class of problems that are much more efficiently coded with an action based formalism (where the states of the state machine can be largely ignored). When you ignore them, you in essence stating that all actions are possible in any given state, which we all know is not generally true.

You can laugh as much as you want about Erik's haha example:
// prints Ha var ha = Ha(); var haha = ha+ha;

but all he states is that you are taking the wrong action in a given state. Nothing more, nothing less.

What does functional programming brings to the table? not very much at all. It expresses in a very convoluted way the relationship between taking an action and the results it might have. It's bit like trying to throw an arrow (even a smart monadic arrow) in a giant maze and hope you'll hit your target.

TLA+ brings a much more natural way to deal with effects, first because it has a clear notion of what a step is and how mutation relates to action. You are free to ignore TLA+, but it's a bit like you are complaining you tried to send rocket to the moon on the assumption the earth was flat and it was really hard to steer the spaceship. I know that we live in world where people believe they can bend reality, as long as enough people believe what they are saying... that that still will bring you nowhere.

So again, I would be really, really, really curious to see what Erik thinks about TLA+, I did connect with him on LinkedIn and asked the question. Never got an answer... After all Dr. Lamport only got a Turing award for it, it's probably not worth his time.

Honestly, @jdubray, you're borderline trolling at this point. You've repeated your arguments dozens of times in this thread, other issues, and elsewhere. You've insulted Dan and the Redux community, you've waved your credentials around, and keep invoking the words "TLA+" and "Lamport" as if they're the Holy Grail and the Answer to Life, the Universe, and Everything. You've insisted that SAM is far superior to Redux, but without writing much in the way of meaningful comparison apps, despite numerous invitations to show evidence. You're really not going to change anyone's mind who isn't already convinced by now. I honestly suggest you go spend your time doing something more productive.

I'm going to close the thread at this point. Comments can continue, I guess, but there's definitely not anything actionable here.

@markerikson I would like to keep this discussion going even if its heading nowhere, the topic interests me quite a bit. It would be better to clear out his toxic comments and keep the good open source discussion going.
Besides @antitoxic (no pun intended) made it quite clear on where the approach hes referring to is useful or not, we know how bad the other issues have turned out because of his interventions so there's no point in listening anymore

It's ok Mark you can remove my comments, why spoil such a brilliant conversation?

@jdubray only you see it as brillant. I think we all feel you try to sell us something without more arguments than "Lamport got a Turing award, so listen to me, I'm right!".

I don't say TLA+ or whatever is not good, and maybe in the future someone will do a better job than you selling it. It's just the way you explain it that does not help us to understand at all, and your permanent condescending tone does not invite me to invest time learning about TLA+.

Erik's paper demonstrate eloquently that imperative programming languages are somewhat broken when it comes to mutating state. I believe everyone agrees on that.

Actually you always use terms like I believe everyone agrees on that in all your posts. This is not something good, helpful or welcoming to people. We don't all agree with you by default, and most of us didn't and won't read Erik's paper. If you can't convince people in a welcoming way, without asking them to read tons of paper before, and making feel them dumb if they don't or disagree with you, it won't help spread your ideas.

You are free to ignore TLA+, but it's a bit like you are complaining you tried to send rocket to the moon on the assumption the earth was flat and it was really hard to steer the spaceship.

This definitively looks like an analogy and certainly not a first principle. Seriously, do you think this statement is to be considered as "brillant discussion"?

I think the discussions are actually more brillant when you don't talk in them, because you monopolize all the attention on TLA+ and actually prevent people not interested in TLA+ to try to send their rocket to the moon with something more concrete, that the average people can understand, like Redux.

Still not saying TLA+ is not worth it, just you are talking about it the wrong way, to the wrong people. Maybe if Erik didn't answer you, it's because he can't understand you either? Maybe you are so clever that only Lamport can understand you? Maybe in the future you will win a Turing award? I don't know, but one sure thing is the current discussion does not bring anything to the table.

Maybe do like @gaearon and create TLA+ / SAM video tutorials for egghead. If you can explain your ideas in a very simple way, in under 2h of videos, and at the end your patterns clicks and feel useful for developers, you win. Redux did that for a lot of people. You only only "tried" to explain theorically why SAM / TLA+ is better than Redux, and provided a awful-looking TodoMVC implementation. We are CS researchers here, we need concrete material. Can you show us an app, written in Redux vs written in SAM, where people would actually feel like the SAM implementation is better? That has not been the case so far.

@Niondir I've come up with a way to use sagas in cases where you need to recover a state.
It's more like a pattern or a set of rules to follow

  • Separate your sagas so each one of them have no more than a single put. If you have sagas with more than a put in them split them and chain them with a call effect. E.g.
function* mySaga() {
  yield put(action1());
  yield put(action2());
}

// split into

function* mySaga() {
  yield put(action1());
  yield call(myNextSaga);
}

function* myNextSaga() {
  yield put(action2());
}
  • Sagas with take effects should be split into the logic that follows the take and the start point of the saga. E.g.
function* mySaga() {
  yield take(ACTION);
  // logic
}

// split it into

function* rootSaga() {
  yield takeLatest(ACTION, mySaga);
}

function* mySaga() {
  // logic
}
  • For each saga create sort of a "checkpoint" feature, which means that at init read from the state and from there call the saga you're in need of
function* rootSaga() {
  // emit all forks and take effects that need to run in the background
  yield call(recoverCheckpoint);
}

function* recoverCheckpoint() {
  const state = yield select();
  if (state.isFetching) {
    // run the saga that left the state like this
  }
}

The explanation why this would work is based on a set of assumptions which are generally true

  • take effects need actions to be dispatched to the store, its safe to always call these sagas at init even if you're recovering because they remain idle until the user interacts with the page and/or sagas are already running
  • Not allowing more than one put call per saga allows for atomic changes in the state, the same way an action can only alter the state in a single way a saga is limited to that capacity as well, which sort of creates a correlation between sagas and action dispatching.

Splitting sagas these way also have some positive _effects_

  • Sagas are more reusable
  • Sagas are more easily altered
  • Sagas are easier to understand

but @slorber:

Lots of people find this "fishing game" sample quite interesting to code since it illustrates first hand how SAM works in its entirety, including a dynamic state representation and the "next-action-predicate". NAP alleviates the need for Sagas, and does not require breaking Redux principle #1 which is a single state tree, but what do I know?

Sometimes when I'm using Ableton Live, the thought pops in my head "would it be possible to write the Ableton Live GUI as a React/Redux app?" I think the answer is no, it would just be too difficult to get acceptable performance. There's a reason it's written in Qt and a reason that Qt has a signals/slots architecture.

I've been using React/Redux with Meteor. I store mongo documents in Immutable.js state by dispatching Redux actions for the callbacks to Mongo.Collection.observeChanges. I recently found that when subscribing to several thousand documents, the UI was extremely laggy on startup because thousands of Redux actions were getting dispatched as Meteor sent the initial subscription results one by one, causing thousands of Immutable.js operations and thousands of rerenders as fast as possible. I assume RethinkDB also work this way, though I'm not sure.

My solution was to create a special middleware that would set aside those collection actions and then dispatch them in batches at a throttled rate, so that I could add ~800 documents in a single state change. It solved the performance issues, but altering the event order is inherently risky because it can lead to inconsistent state. For instance, I had to make sure the subscription status actions were dispatched through the same throttler, so that the subscriptions didn't get marked ready before all the documents had been added to the Redux state.

@jedwards1211 I've had a similar problem (lots of small messages that would compose the initial data).

Solved in the same way by doing a batch update. Instead of a middleware I've user a (small) custom queue mechanism (push all updates in an array, and at regular intervals if there's anything to update update all of them at the same time.

@andreieftimie yeah, it's trickier in my app because there are some UI interactions that require immediate updates, like dragging and zooming plots around. So I have to have a middleware layer to decide whether to dispatch an action immediately or put it in a queue for batch dispatch.

For what it's worth, I had an idea of creating "branches". It totally defies the single store principle, but it seemed like a good compromise.

https://github.com/stephenbunch/redux-branch

@jedwards1211 I think that as apps grow in complexity there's eventually the need to break out certain types of data and certain types of updates so that certain stores aren't handling the full load.

Let's say that there's some core state that you want sync'd with the backend, there's some animation state so that your app UI components are displaying correctly according to the current actions being performed, and maybe you have some debug data that you're tracking separately.

In this contrived example, it's certainly possibly to build all of this out of a single Redux store, but depending on how you architect it, there'll be some blurry lines between concerns of various branches of the state tree, and maybe lack of clarity regarding which state a given action is intended to interact with.

I think it's perfectly reasonable to use different state management strategies according to how you want state to be scoped and what performance characteristics you expect out of it. Redux is suitably geared towards state in which you may want to rollback or replay actions, or serialize and return to later. For animation state, you can use a mutable store or a reactive store if you want—there will be less overhead that way and you won't be polluting your application state with this transient (but still necessary) interaction state.

Real quick, I want to make a point using the upcoming React fiber architecture. Here's a link, apologies if my understanding is a bit out of date. Roughly, the fiber architecture acknowledges that there are different kind of updates that will propagate through a React component tree, and there's different expectations regarding when and how these updates will perform. You want animation updates to be fast, responsive, and reliable, and you don't want big interaction updates to introduce jank in your animations and such.

So the fiber architecture breaks down updates into work packets and schedules them according to a priority based on the work being performed. Animations are high priority—so that they're not interrupted, and slower mechanical updates have a lower priority.

I've skipped over a lot of details about React fibers and probably gotten some things wrong in the process, but my point is that this is the kind of granular approach I think is necessary for your data store with different types of data.

If I was building a complex app today, I'd start with a Redux-like top-level store class backed by a few different stores. Roughly:

  • Top level data store has a queue for incoming actions
  • Actions are either for animation actions that should get dispatched quickly, or application interactions that have lower priority
  • Animation actions in the queue get dispatched to an observable backend from a requestAnimationFrame loop
  • Interaction actions get dispatched to redux when the animation actions from the queue are complete

This story seems pretty close to a complete state solution based on Redux. Redux is suited to data you want to view after a sequence of actions, and there's a lot of other state out there that needs careful handling as well.

@jsonnull I've never had any need to store animation state in a store -- local state has always been ideal for my animation use cases. I'd be curious to know if there are some use cases that local animation state is completely unsuited for. It sounds like they have great ideas for the new architecture though.

@jedwards1211 There's a couple cases I can think of where local animation state doesn't work. I'll grant you, they're not cases you'll run into with every application, but I think they come up often enough.

In one case you're using a library other than Redux where you don't have local state. (Ok, yes, I know I'm cheating a little here.) If you're using a very lean hyperscript approach, no virtual dom, pure functional components, then instead of local state you're going to have to pass some animation state to the root node or whichever node you're re-rendering.

In this case, having a state tree with a few animation details set will allow you to work around lack of local state so that you can trigger animations, have the animations run for a duration, and more.

The key thing here is that there's two ways to do these animations—do them as part of your render and keep the "UI as a function of state" metaphor intact, or mutate the DOM separately. Almost always the better answer is to just re-render instead of mutating.


Now for an example where you do have the ability to keep some animation state local—building a browser game with a React-based UI on top of it.

  • Usually you'll drive the game using ticks... the game time starts at 0 and is incremented every frame. Core game animations may be done on canvas or in WebGL where there's no local state, so you're going to base animation start time and progress based off of these ticks.

Ex: A sprite character has an animation comprising of 10 frames, and you want to play out the animation over ~400ms, so you're going to change the sprite drawn every 2 ticks or so.

Your ticks could also be timestamps if you want higher resolution.

  • Your game can also pause, in which case you may want to halt some animations done in the React UI side.

In this game example, what you don't want to do is increment your ticks as part of a tick action... instead you want to increment ticks separately, possibly in an observable structure. And when you dispatch Redux actions such as keyboard character movement or attack, you'll pass the current tick or current time to them, so that you can record which tick an action occurred at and your UI components can record locally what time an animation began.

Now you have separated the global animation state from the interaction state, and if you want to do a "replay" using only the Redux state or by replaying actions, you can, and you're not burdened by incrementing ticks as part of your state tree.

Generally speaking, it's also much better to coordinate animations by passing start/stop times through Redux and letting components maintain the rest of the state locally.

This does not only apply to games. Any extended sequence of animations might go through this, such as if you wanted to do a series of long-running animations on a site using React.

@jsonnull cool, that's a great example, thanks for sharing that.

I would simply say - in Redux you just can't store you state objects with links to each other. For example - user can have many posts and post can have many comments and I want to store those objects in a way:

var user = {id: 'user1', posts: [], comments: []}
var post = {id: 'post1', user: user, comments: []}
user.posts.push(post);
var comment = {id: 'comment1', post: post, user: user}
post.coments.push(comment)
user.comments.push(comment)
appState.user = user

Redux doesn't allow usage of object hierarchies with circular references. In Mobx (or Cellx) you just can simply have one-to-many and many-to-many references with objects and it simplified business logic many times

@bgnorlov I don't think anything in the redux package prohibits circular references like you're talking about -- they just make it more difficult to clone your state in your reducer. Also, it might be easier to make changes to state with circular references if you use Immutable.js to represent your state, though I'm not sure.

You could also model relationships in your redux state by using foreign keys, though I understand this is not as convenient as using ORM-like object references:

var user = {id: 'user1', postIds: [], commentIds: []}
var post = {id: 'post1', userId: user.id, commentIds: []}
user.postIds.push(post.id);
var comment = {id: 'comment1', postId: post.id, userId: user.id}
post.commentIds.push(comment.id)
user.commentIds.push(comment.id)
appState.userId = user.id
appState.posts = {[post.id]: post}
appState.comments = {[comment.id]: comment}

// then join things like so:
var postsWithComments = _.map(appState.posts, post => ({
  ...post,
  comments: post.commentIds.map(id => appState.comments[id]),
})

@jedwards1211 to clone state with circular references in redux, reducer must return every new copy of an object which affected by changes. If reference to object changes the related object also need to be a new copy and this will be repeated in а recursive way and it will generate whole new state on every change. Immutable.js can't store circular references.
With normalization approach when some event handler needs some data it requires every time to take object by its id from global state. For example - I need to filter task siblings somewhere in event handler (tasks can have hierarchical structure) With redux I need to dispatch thunk to get access to app state

var prevTask = dispatch((_, getState)=>getState().tables.tasks[task.parentId]).children.map(childId=>dispatch((_, getState)=>getState().tables.tasks[childId])).filter(task=>...) [0]

or with selectors

var prevTask = dispatch(getTaskById(task.parentId)).children.map(childId=>dispatch(getTaskById(childId)).filter(task=>...)[0]

and this boilerplate turns code into mess compare to mobx version when I can simply write

var prevTask = task.parent.children.filter(task=>...)[0]

@jedwards1211 , @bgnorlov : FWIW, this is one of the reasons why I like Redux-ORM. It allows you to keep your Redux store normalized, but makes doing those relational updates and lookups simpler. In fact, that last example is basically identical with Redux-ORM.

I just wrote a couple blog posts describing Redux-ORM basics, as well as core concepts and advanced usage.

@markerikson cool, thanks for the tip!

@gaearon

One thing to note is that we don’t intend Redux to be used for all state. Just whatever seems significant to the app. I would argue inputs and animation state should be handled by React (or another ephemeral state abstraction). Redux works better for things like fetched data and locally modified models.

I found this comment to be so incredibly useful. I know I'm late to the party, but maybe that's part of my point. Over a year since that comment was posted and this is still the only place I've seen this idea expressed.

Coming from an angular and decidedly non-flux/redux background it is very hard to formulate that idea on your own. Especially when a lot of examples still create actions for every keyup in a text box. I wish somebody would put that quote in 50px text on the top of every Redux documentation page.

@leff Agreed. I'd synthesized that exact idea some time back, but it doesn't get called out enough. Even if you're using Redux for history management, there's often a lot of ephemeral state that doesn't need to burden your Redux-style state management.

That won't necessarily be a surprise to folks who've had the opportunity to work on, e.g. a mature desktop app that does rich undo/redo. But to the oceans of newcomers coming to these ideas via Redux, it'll be a revelation.

@bgnorlov that's a good point that it's impossible to store circular references with Immutable.js, I never thought about that! I think it's a nice feature though.

These days we do tend to store almost everything in Redux, even if it's transient view-specific state that we never use outside its context (e.g. state for a redux-form instance). In most cases we could move such state out of redux without suffering any problems. But the upside is it's there in case we ever need external components to respond to it in the future. For instance, we could have some icon in the navbar that changes when any form has errors or is submitting.

I'd say the most important thing to keep in mind is that putting all normalized state in Redux (i.e. anything that needs to be joined together to render views) will make it easiest to use. State that doesn't need to be joined with anything can probably live outside of Redux without causing you difficulty, but it usually doesn't hurt to put it in Redux either.

FWIW, the docs do point out that not everything has to go into Redux, per http://redux.js.org/docs/faq/OrganizingState.html#organizing-state-only-redux-state .

There's been quite a bit of recent chatter online about what Redux is "appropriate for". Some people see benefits from putting literally everything into Redux, others find that to be too much hassle and only want to store data retrieved from a server. So, there's definitely no fixed rule here.

As always, if people have ideas for improving the docs, PRs are totally welcome :)

If you ever need to reuse reducers and be able to allocate "sub-states" in your redux state I developed a plugin to do it effortlessly (with React integration)

https://github.com/eloytoro/react-redux-uuid

@eloytoro IMO if you have trouble with some 3rd-party reducer having hardcoded action types or location in the state, you should open an issue in the project...before long it will become unacceptable design practice as people learn the reusuable reducer/action creator pattern.

@eloytoro I was going to write something like that. Thank you very much for the reference!

@jedwards1211 I think you got the wrong idea. I haven't mentioned 3rd party reducers or issues with them in any way, just trying to showcase how my snippet can solve the issue for making reducers reusable in collections with dynamic sizes

@avesus hope it works for you

@eloytoro ah, that makes more sense.

Last year I created a fairly complex game using Redux. I have also participated in another project which doesn't use Redux, but instead a custom-built, minimalistic framework which separates everything into UI Stores and Server Stores (including inputs). Without going into too many details, my conclusions so far are the following:

Single state is always better, however, do not over do it. Getting every keyDown() through the store and back to the view is just confusing and unnecessary. So transient states should be handled by local components states (such as React's).

I think that since the redux and react inflamed the observable web, the quantity of good animation on the pages declined.

@mib32 you may be right! Hopefully people will eventually get used to crafting good animation in React.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CellOcean picture CellOcean  ·  3Comments

dmitry-zaets picture dmitry-zaets  ·  3Comments

ramakay picture ramakay  ·  3Comments

jbri7357 picture jbri7357  ·  3Comments

captbaritone picture captbaritone  ·  3Comments