-
-
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
Connect HOC misses store changes when "offscreen" #1940
Comments
Do you think such an API is a good idea? I got an impression that the whole strict effects stuff was introduced specifically to avoid subscriptions in hidden components etc. As to the shared Replay - I couldn't resist taking a look at it 😅 I'm not sure if I understand the issue correctly in full so take my findings with the grain of salt. At the moment, Note that you click on that React tab somewhere around 0:27 of this recording and this The very last call to this We can see here your click around 0:27, this last call to So it looks as if React just reused the hidden content, without rendering it/resubscribing to it. I assume that the lack of rerender is good when we know that nothing has changed inside but it shouldn't skip resubscribing it because it never gives a chance for this component to "fix" its state. Since you have fixed it by migrating to the hooks API (which is also based on |
@Andarist have I mentioned lately that you're awesome? :) Yeah, my initial thoughts here are:
|
It has been a couple of days since the last time 🤣
Yeah, I wouldn't be totally surprised by that :P
But because of this, I'm inclined to believe that this is a React issue here. Whatever memoization/hacks you have in place shouldn't matter here because they could only mess up an active subscription from React but it seems like React has unsubscribed from this store and never subscribed back. So it didn't even give you a chance to screw up :P |
Maybe I'm misunderstanding something, but why would React not unsubscribe from the store in this scenario? There is (at least currently) no proposal that I'm aware of to stay subscribed while offscreen/hidden. The thing that surprises me is that the connected component doesn't re-render when being shown again (so no chance to check for a missed subscription). Then again, the component with the new hooks did. That seems like an interesting difference. Did the connected component not re-render because of a React screw-up or a bug in Redux memoization? Did the component with hooks re-render because of missing memoization or because it was working as designed? 🤷🏼 |
My guess: It's being called with the same props as last time and it's wrapped in |
I meant that this hidden component was unsubscribed (although I didn't fully confirm that, but it's what should happen) but I never have seen it being resubscribed on this Replay. That indicates to me that it's possibly a bug in React. But, of course, I could also draw incorrect conclusions from the recording altogether.
To be more precise (if my findings are correct) - it wouldn't really have to rerender this component unconditionally. In this situation, it should resubscribe external stores, and a change in the snapshot/selected value should lead to a rerender. OTOH, we can't subscribe immediately when showing the component as this is a side-effect, and thus React needs to defer that to a commit phase. So the only way to check if there is a change is to actually run the I've located the usage of the |
We're getting kind of deep into the weeds of speculation here. Bugs in React are always a possibility, but at the same time– I am pretty confident that the team would not have missed something so obvious as "sync external store will miss subscriptions that fire during Offscreen" so I think there must be something else going on here. FWIW I spent a bit of time trying to recreate this in a Code Sandbox a few days ago but was not successful. Perhaps I overlooked something silly, or perhaps it's more complicated to recreate: One interesting thing the Code Sandbox I created above shows is that React actually is still updating the offscreen components when they're hidden. (You can inspect the DOM and verify that their content is being updated). Recording a profile confirms this (note the high-pri render and the separate Offscreen priority render): So perhaps a key difference here might be if @markerikson's component is actually not supposed to unsubscribe in the offscreen state? |
I'll try looking at the actual replays tomorrow, but off the top of my head, I would expect this to unsubscribe. React-Redux v8 defaults to using the That implementation relies on Assuming that React runs hook cleanups when a component goes offscreen... then yes, I would most definitely expect it to unsubscribe at that point. But in turn, I would also expect that React would re-run the effect hooks when the component is brought back in, and that specifically checks for initial data. So, those are my expectations. Clearly something about those is not valid :) |
I didn't yet have time to fully validate this but I think I got the fix for this: #1941 What @bvaughn has mentioned 2 comments above got me to the conclusion that this is the most likely culprit. It seems that at the moment
So how this has affected React Redux? react-redux/src/components/connect.tsx Line 630 in 720f0ba
But at the same time the passive subscription from the So in an effect of all of this - |
AHHHH, that's fascinating! So apparently this was my mistaken assumption:
or at least for passive effects. Out of curiosity, what happens if we were to alter the build so that it aliases |
What scenario are you thinking about here? Btw. when using React 18 the shim should proxy to the "native" implementation - so I would say that "the result is always the same". |
Oh yeah, that's a good point - that explains why I was seeing 0 hits for the actual shim logic, and 1 hit for the outer part of the shim file :) (forgot that was the case - I was fixated on the fact that we do always load the shim library, which does add to bundle size) |
Just to be clear, my Code Sandbox is running react-dom However the version of React that Replay is currently building with is |
I've successfully reproduced this issue in a standalone local project, with 5 components:
I can confirm that just clicking "+" with the "Dummy" tab active renders the first three components, but not the |
Okay, I've dug into this further. My final conclusion: This is really a bug with the experimental build of React (2022-03-25) that Replay is currently using, and this is fixed in later experimental builds of React! I ran my same sandbox app with a current experimental build of React ( I can confirm that now the app behaves "as expected":
Or, to put it another way:
Given that, I think we can safely close this as " Thank you to @bvaughn for recording/filing this, and @Andarist for helping investigate! |
Some additional notes from Andarist on Twitter: https://twitter.com/AndaristRake/status/1559989920323833857
So this may be something we have to revisit later |
I'm afraid that this should be reopened 😅 Based on the quote above I've found this to be an issue with the current version of React too. Repro: https://codesandbox.io/s/goofy-kapitsa-wkojvp?file=/src/App.js Scenario:
I've also prepared two codesandboxes to demonstrate how layout & passive effects behave in basic scenarios. If you suspend initially then both types of effects are only setup after the value gets resolved and when the actual content gets rendered (we switch from a fallback to the content here) However, if you suspend an already committed tree then layout effects are being "disconnected", we switch to a fallback, the content gets merely hidden (but not unmounted). When the value resolves we switch back to displaying the content and layout effects are set up again. Note that the same doesn't happen for passive effects - they stay "active" in such a suspended tree. |
sigh yeah, thanks for investigating further :) |
This might actually be fixed by #2068 ? |
Unfortunately not - I can still see the problem with the updated react-redux version: https://codesandbox.io/s/billowing-resonance-p88lmr?file=/src/App.js |
What version of React, ReactDOM/React Native, Redux, and React Redux are you using?
Here is a Replay recording of this bug with some annotations from me:
http://app.replay.io/recording/replay-failing-reactdevtools-e2e-repro--3351989d-95d4-4035-80af-07d813a623aa
Here is a Loom (it's long) where I use Replay to investigate the problem:
https://www.loom.com/share/3d60c14466184b03acc9af7b80e9eff8
Also note that this issue is specific to an experimental React API,
Offscreen
. The React team has published some early documentation about this API but it has not been released in a stable form yet.What is the current behavior?
A connected component misses updates that are fired while it is inside of a hidden
Offscreen
component.I believe this is because the component unsubscribes when it is unmounting and does not check for missed updates when it is later remounted. (The selectors passed tot he map-state-to-props function were not executed.)
What is the expected behavior?
The expected behavior is perhaps up for debate. Should the component process Store updates (at a lower priority) while hidden or should it wait to render them only if it's later re-shown? In a sense, this question isn't worth debating yet because I don't think React currently provides an API (even an experimental one) to stay subscribed while in a hidden state.
That being said, I think the currently expected behavior is that when then
Offscreen
tree is later re-shown, React Reduxconnect
should check the Store for the latest values and re-render if they have changed since the component was last rendered.Which browser and OS are affected by this issue?
Presumably all of them
Did this work in previous versions of React Redux?
The text was updated successfully, but these errors were encountered: