-
-
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
Nested connect
updates are not batched
#1510
Comments
My first guess is that it's specifically because you're dispatching in What happens if you move the call into a single button click, instead? |
Unfortunately the same happens inside a click handler: https://stackblitz.com/edit/react-redux-batching-nodjcz?file=index.tsx |
Hmm. Somewhat surprised by that. That said, all the interactions between React's rendering, React's batching, and how React-Redux is implemented make this a very complex topic. Is there a particular issue you're trying to solve, or was this more of a "why does it work this way?" kind of question? |
I'm profiling React re-renders on unsplash.com, to try to improve our Time to Interactive, and I noticed that there are many more React re-renders than I had expected. I reduced the issue down to this. My hope is that I can batch those into a single re-render, to improve our TTI. |
So the second thought here is that it has to do with how React-Redux is implemented internally. We have to enforce top-down updates, and use an internal If I add a second instance of I'm guessing that if you add a third level of nesting, you'd see a third commit. In other words, there will be N commits per dispatch, roughly equating to the depth of the component tree, as the nested connected components cascade the subscription notifications downwards. Just for kicks, you might want to try setting up an example app with 4 or 5 levels of nesting, then try doing profile comparisons between React-Redux v5, v6, and v7, to see how they behave. |
@markerikson Thanks for the explanation. That makes sense.
This was the part I was missing. I tried patching React Redux to switch I'm now wondering why we need to use react-redux/src/utils/useIsomorphicLayoutEffect.js Lines 5 to 10 in 77a2044
I'll have to chew on that for awhile 🤔 |
Yes, exactly as that comment points out. In fact, we recently had a bug where we failed to correctly call Note that React-Redux v6 was "better" in this regard because everything was done as part of React's normal rendering process, and thus we got top-down behavior for free without any of this subscription work. But, because all that work was being done while rendering, and state propagation was happening via context, the overall work of rendering was much slower, and thus we had to rewrite things for v7 (per #1177 ). |
are you sure? it seems very strange, because useEffect has the same commit phases but with async beetween commits. |
@markerikson after some debug, it seem that batchUpdates as no effect when "setting states on" didUpdate/layouyEffect because the flow is while this flow resolve the selector stale props issue, it cannot de-facto "batch-updates!" useSelector should not have this problem, as there is no nested-subscription. (if all React elements are using only useSelector (no connect)) i'm quite sure that nested-subscription invalidate "batch-updates". hum, maybe there is no way to resolve BOTH stale props and batched updates using connect. my opinion, is that stale props issue should be resolved on next-render, while but anyway connect is de facto deprecated with useSelector, so this is legacy issue. :) |
@OliverJAsh @markerikson i made a sample without redux and react-redux. it seems a issue or expected behavior of unstable_batchedUpdates |
Yes, because it's that "async" aspect that is the issue. Imagine this sequence:
In that case, the newly-mounted component has missed the notification that an action was dispatched, and won't re-render correctly. That's exactly the bug that I was talking about that we just had with RN due to mis-configured feature detection.
No, |
@salvoravida : replied over in the React issue, but pasting here for context: The sandbox isn't actually testing the behavior you think it is. First, React already wraps all event handlers in Second, either way, that button click event handler is only queueing one state update, not multiple, so there's no change in behavior. Third, each of those child components is adding an additional state update in the commit phase, but that's after the On the other hand, what React-Redux does is call |
Sorry I do not think so. React redux call notifynestedsub under uselayouteffecct that is exact the problem of this issue. Open profile of this original nested redux update with connect. I have debuged them. |
What you're missing is the last part of my previous comment. |
Yes I know. But here the problem is that if you have 100 nested connect component, you will see 100 commit in profile. Batched updates get only the flat connected component on each level. |
Right, again, that's my point. With how the With React-Redux v5, which used the same |
Right the problem is that. Batching is only per level. Not for all tree interested of updates. So if we have only a tree of connected component there will be no batch. Is that an expected behavior of unstable batched updates? Hum. |
I'm not sure what you're saying there. Specific example. Let's say we have a tree of connected components that looks like this:
Let's also assume, for this example, that all components are connected and reading a single counter value out of the store, but not passing any props to their children.
So, I would expect 4 total commits from that tree. |
right. that's exact what @OliverJAsh means in his "Nested so 1 store update 4 commit. neste batched due to nested subscription are not batched into 1 commit. for example in that Tree A-G using useSelector there will be only 1 commit, and ONLY if in that tree there is some parent steal props there will be a re-commit. this is the point. |
And I'm saying there's two different aspects here:
We currently do the first, but not the second. |
I'm going to close this as a WONTFIX for now, largely because we're encouraging folks to stop using If someone still feels strongly about this please comment, or even better file a PR to improve behavior here. |
Do you want to request a feature or report a bug?
(If this is a usage question, please do not post it here—post it on Stack Overflow instead. If this is not a “feature” or a “bug”, or the phrase “How do I...?” applies, then it's probably a usage question.)
Bug
What is the current behavior?
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to a CodeSandbox (https://codesandbox.io/s/new) or RN Snack (https://snack.expo.io/) example below:
https://stackblitz.com/edit/react-redux-batching-xkhtq8?file=index.tsx
What is the expected behavior?
Expected: 2 commits:
Counter
andReduxCounter
update, triggered viadispatch
insideCounter#componentDidMount
Actual: 3 commits:
Counter
update, triggered viadispatch
insideCounter#componentDidMount
ReduxCounter
update, triggered viadispatch
insideCounter#componentDidMount
IIUC, there is no reason why 2 and 3 can not be batched into a single React re-render?
Which versions of React, ReactDOM/React Native, Redux, and React Redux are you using? Which browser and OS are affected by this issue? Did this work in previous versions of React Redux?
Update: note this issue does not occur when using
useSelector
. https://stackblitz.com/edit/react-redux-batching-zb1wxv?file=index.tsxThe text was updated successfully, but these errors were encountered: