-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix issues with stale props #99
Conversation
9f02baf
to
7fa86d0
Compare
@epeli this brings us back to the way things were before #1 which as @gaearon says makes the worst case performance the default. I'm inclined to leave the existing behavior as is. If we did adopt this form I would hope that it be made optional and opt-in much like the 2nd arg props parameter to the first two connect arguments. |
Not able apprehend right away every point made in #1 but I'd like hear why this would be the worst case compared to the current implementation because in it |
I could be wrong because I haven't tried running it with your PR but I believe that this change will make even users of the Is that not true? |
1738478
to
0225999
Compare
All tests now pass.
If I understand you correctly the test I will do some real world tests later today to see if I missed some regressions. I'd love see other test this as well! I also added a new test which makes sure that the wrapped component does not re-render on every state change. Only when the mapState creates a new state. |
0225999
to
cc5f049
Compare
Wrote a failing test for the issue at hand and learnt something new about React. React only batches setState calls during event handlers and because this fix relies on the batching it means that the original issue can still occur when the store is updated from somewhere else. Any ideas how common that is? Could it be possible to somehow force React in to the batching mode when store updates? In the other news I'm begining to be fairly confident about the performance here. Waiting for feedback. |
To answer myself: Yes with In that issue sebmarkbage mentions that React has a plan to move batching by default. Can you @sebmarkbage confirm that that's still the case? If so then that issue will be resolved by itself with React update. Until then redux-batched-updates middleware can be used as a workaround. Anyone wanting to help and test this I pushed a compiled version to |
@epeli took a better look and you I retract my earlier statements about regressing performance-wise. However regarding the lack of consistent batched updating is it true then that for this PR to not regress in any case one needs to use either the redux-batched-updates middleware or does the lack of that simply return us to the edge-case broken state that we are in with the reported issue #86 ? |
It simply returns to the edge-case broken state. So currently this PR just makes the edge-case less likely and goes completely away with the redux-batched-updates middleware. Incorporating batched updates into Redux was discussed in reduxjs/redux#125 and it seems that the main reason not to do that was the fact that it was available only as a React addon. Could it be considered for react-redux as it's now available in the React object? |
I'll take this back. It was available only in the 0.14 beta. It has been moved to react-dom in 0.14 RC. I guess we don't want to depend on that because react-redux can be used with React Native and it makes no sense there. Nevertheless I don't haven't seen / heard any downsides in merging this PR. Without it it is impossible to workaround the issue. |
Is it absolutely necessary to have the intermediate component here? |
Not absolutely necessary. I think the same optimization could be implemented manually in the connector component by invoking the mapState function in shouldComponentUpdate. |
My opinion is leave out the PureWrap and let that be a call site optimization. Lots of people connect pure render mixin'd components so I don't think it grants a whole lot in terms of benefit for the extra complexity of code (unwrap function for instance) plus the variable way the connected component would be represented in the component tree (normal connected components will now have 3 tiers intead of just 2 in the devtools explorer) Neither of these downsides are huge but then neither is the upside so simplicity wins? |
most mapState functions will be fast but i wonder if adding the extra call would be worth the mild improvement you get by avoiding some updates. I can imagine that this might improve the already decently fast cases but make the minority of slower cases even slower. just food for thought |
The devtools argument is really good argument against the PureWrap. I'll remove it on Monday. Seems that the batching will become default in React at some point:https://mobile.twitter.com/sebmarkbage/status/642366976824864768?refsrc=email&cn=cmVwbHk%3D |
To clarify: I don't promise I'll merge this PR yet :-). I definitely want to avoid:
|
Currently I don't see any reason why both would not be avoidable. Perf should be OK already. I'm now busy for couple days but will work on this after that. |
d930c48
to
5ed9f40
Compare
I rewrote the entire pull request and force pushed it to this PR branch.
As far as I understand the performance should be as good as in the master. Although because there is no performance test suite so I cannot be 100% sure. A new compiled version is again available in |
Good job! I've been trying to avoid Do you have any idea how we can test perf regression, if any? Maybe we can put this into https://github.com/evancz/todomvc-perf-comparison and hope the benchmark isn't too skewed? |
Thanks!
Sadly it's exactly
I think http://benchmarkjs.com/ by the Lo-Dash creator jdalton is the tool for the job. We've used it for |
Do you have the time capacity to try to benchmark before/after? |
So I created #104 for that. Just one simple test to see how store changes perform on a mounted component. Results of three runs on the current master:
and with this PR
Not seeing any significant differences. But this is just a one test. |
Something still blocking this? |
Can you please write release notes and upgrade instructions for this change? From my understanding, this potentially can be a breaking change, so we'll jump to 3.0, but we need to tell people which exactly patterns would fail which worked before. And of course we need to explain which patterns, previously failing, would work now with this change. I'll release it as |
Out as |
One correction to the release notes:
It's only relevant if you have nested connect()ed components _and you use the Also I would like to recommend usage of |
I released 3.0.0 but I think I'm misrepresenting the change again. ? |
The I would put it like this:
|
Oh, a collaborator hat, thanks :) Updated it. |
Thanks! |
Fix for #86
The idea in this change is to delay executing
mapStateToProps
until the render call. The render call is forced for every state change by setting the store state to the component state for every connect wrapper. This works because state changes are batched in React. Which means that when the render and there for themapStateToProps
is executed the state and any props based on it are consistent across the full component tree. Which is awesome!Only concern that I have with this is performance. That's why in the second commit I add a pure render wrapper component (
PureWrap
) which makes sure that the original component is only rendered when themapStateToProps
actually produces a change. Not sure whether this is better or worse than the original. But even if this is not that performant I'd prefer it because it avoids whole class of weird edge cases.Anyone wanting to help and test this I pushed a compiled version to
epeli/react-redux#fix86
. Put that in your package.json as thereact-redux
version number.