3

Following code using simple Redux API:

    // Dummy Reducer that just returns previous state
    const counter = (state = 0, action) => {
      return state;
    } 

    // Store
    const { createStore } = Redux;
    const store = createStore(counter);

    const listener = () => {
      console.log('Listener called...with ' + store.getState());
    };

    // Listener
    store.subscribe(listener);

    // Manually dispatching actions
    store.dispatch({ type: 'DUMMY' });
    store.dispatch({ type: 'DUMMY' });
    store.dispatch({ type: 'DUMMY' });

produces following output:

Listener called...with 0
Listener called...with 0
Listener called...with 0

My question: If nothing changes in the store, why listener is being notified as if something changed. Isnt is unnecessary and counter productive? Lets say, the listeners are views like React Container Components. They will try rerendering unnecessarily right ?

Or am I missing something?

In case of Flux, I feel we have higher flexibility in terms of whether to publish the change from the store or not. Is this a con for Redux over Flux ? Or am I missing something?

stone
  • 8,422
  • 5
  • 54
  • 66
Arun Kandregula
  • 655
  • 3
  • 8
  • 18
  • 2
    I don't think redux works by doing a compare of state before & after an action. I would think it relies on an action being called to assume state has changed even though it might have *not* changed. Why? Because rendering is likely to be much less costly (considering React is running its own after/before comparison) than doing your own custom compare (especially knowing that React would still do its own afterwards). Using the triggering of an action to assume state has changed is thus fair AND smart, imo. – Thomas Hennes Oct 21 '17 at 21:23
  • 1
    Redux does not even keep a previos state. It also does not have enough knowledge about the data to implement correct equality. Thats not its responsibility. – Sulthan Oct 21 '17 at 21:33
  • @Jaxx Correct me if I am wrong. As per redux principle, should store.getState() always return a different instance each time as we want store.state to be immutable? Lets talk about 2 cases : Case1: store.getState() returns same object but different reference because of immutability. Case2: store.getState() return same reference each time. In Case1, I believe react views will be rerendered although store's state did not change as it belives state tree has changed. How to prevent react views from rerendering in this case ? – Arun Kandregula Oct 22 '17 at 14:51
  • 1
    My main question is in redux, as store reducers always return new instances of state, even if the state did not change, react will end up rerendering the views. Isnt it? Correct me if I am wrong. – Arun Kandregula Oct 22 '17 at 14:58
  • @ArunKandregula It will, but you also have to consider the React side of things, i.e. the shadow DOM and how React will only modify the *actual DOM* if changes actually happened, which will not be the case if your state didn't change (and by change here I mean a change in value, not in reference ; only a change in value will in turn change what the component is rendered to). – Thomas Hennes Oct 22 '17 at 16:09
  • @Jaxx My question is even in the case of virtual DOM, lets say the container components will convert state into props and pass them to our react components. Now we have to check if the value of properties changed or not. If the prop objects are deep nested objects, we have to do deep comparision atleast. Deep comparision of big objects is costly even if they are same values but different instances. Please clarify. – Arun Kandregula Oct 22 '17 at 19:21
  • @ArunKandregula Hmm, again I disagree. First of all, in most use cases, when you pass props into a component and these props change (meaning their *value* has changed), React will handle re-rendering without you having to do anything. In these rare use cases where you *have* to do something (using some props to instantiate state changes within the child, react-router with a URL change without routing to a new component), you're only interested in very specific props, there's usually no need for a deep compare. I'm not sure what point it is you're trying to make here. – Thomas Hennes Oct 22 '17 at 19:48
  • @Jaxx I am specifically referring to extra work one has to do in componentWillReceiveProps or shouldComponentUpdate even if nothing changed in the store. Refer to https://stackoverflow.com/a/36214442/318116. You ll get the context I am taking. And I am saying that if old state and new state is same but different instances, we have to do deep comparision which is costly to finally figure out nothing changed and render is not needed. – Arun Kandregula Oct 23 '17 at 00:43
  • @Jazz I am talking about extra optimization where we return false from shouldComponentUpdate when we know that state did not change in store. How do you do that without deep comparision of props passed down from Container components ? – Arun Kandregula Oct 23 '17 at 02:47
  • @ArunKandregula Ok, let me put this another way. As the link you provided above points out, it's a *deliberate* design decision. Let's say you are correct, and in 1% of use cases, we do extra computation for no reason (by running a deep compare algorithm on strictly identical data). The alternative would be to do (useless) extra computation on the other 99% of use cases, so that we spare ourselves the 1% overhead. That's just not an efficient trade-off. We can argue about the 1% vs 99% ; perhaps it's 5% vs 95%, or 0.001% vs 99.999%. But the rationale stays the same. – Thomas Hennes Oct 23 '17 at 09:24
  • @ArunKandregula Also, if your deep compare operations (in a shouldComponentUpdate) are so complex they exceed the time required to re-render that same component, you're doing it wrong. It's much more efficient to assume the data *has* changed and re-render regardless. – Thomas Hennes Oct 23 '17 at 09:37
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/157300/discussion-between-arun-kandregula-and-jaxx). – Arun Kandregula Oct 23 '17 at 17:48

1 Answers1

2

According to the docs, store.subscribe():

Adds a change listener. It will be called any time an action is dispatched, and some part of the state tree may potentially have changed.

Many (most?) popular redux patterns do not require developers to use store.subscribe() at all. See react-redux and redux-saga.

However, it's a good question whether store.subscribe affects performance of such frameworks.

With react-redux, the most popular redux framework for react, container components don't do long-running tasks like http requests; those are typically handled by dispatched actions. So container components tend to be very high-performance, simply pulling data out of simple objects in the store. When their output doesn't change, then the associated view components won't re-render.

stone
  • 8,422
  • 5
  • 54
  • 66
  • My main question is in redux, as store reducers always return new instances of state, even if the state did not change, react will end up rerendering the views. Isnt it? Correct me if I am wrong. – Arun Kandregula Oct 22 '17 at 14:58
  • 1
    The React-Redux `connect` function implements optimizations to detect state changes based on object references. So, if a `connect`ed component sees the same root state object as before, or the `mapState` function returns the same values as the last time, then the component will not re-render. – markerikson Oct 23 '17 at 19:54
  • I feel when nothing changed in the store, the store should return same instance of state (instead of different instance with same value) so that react components or views dont have to do deep comparision. Isnt it ? – Arun Kandregula Nov 12 '17 at 01:37