-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Data: Rerun selection if state changed during mount #11777
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subscribing in constructor can cause subscriptions to leak in two cases:
componentWillUnmount
never is.render
methods) and suddenly a higher priority update is scheduled that invalidates the work that's been done. Then the unfinished low-priority work will be thrown away and restarted. In such a case, constructor is called, but thecomponent(DidMount|WillUnmount)
methods aren't.The correct place to subscribe is therefore always
componentDidMount
.Then, of course, we might miss a store update that happened between constructor and
componentDidMount
.react-redux
solves that by rerunning the selector (i.e.,mapStateToProps
) incomponentDidMount
and callingforceUpdate
if anything changed. That's the stable v5.1.1.In master, they have an unreleased beta version where the Redux provider and consumer are rewritten using the new React context API and where the
connect
-ed consumers don't subscribe to the store at all. Just the Provider does. That could be an inspiration for the@wordpress/data
internals, too 🙂Conclusion: this patch solves the bug that @Tug discovered, but other subtle issues are still present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subscribing in the constructor is important for another use-case. It's important that the parent's component rerenders before its children do. For example when you remove a block, you don't want its inner components to rerender, you want the block list to rerender first to remove the block before the selectors of a removed block get called.
There's probably a better way to fix it but moving to componentDidMount causes the components to rerender in the wrong order because
componentDidMount
of children components runs before parents. I wonder howconnect
solves this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found a comment that I believe explains it: reduxjs/react-redux#1079 (comment)
Makes me wonder if we should use a per component listener?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In
react-redux
, nested connected components don't subscribe directly to the Redux store, but there is a nested hierarchy ofSubscription
objects and each child component subscribes to the parent'sSubscription
. Only components that don't have any connected parent subscribe directly to the store. Then theSubscription
object makes sure that the listener of its owner is called first, and the listeners of the children are called after it. Something like this:The parent subscription object is passed down to children in context. And then each child in turn passes a different parent subscription to its children.
It's very clever and satisfies all the subtle requirements: no leaks, parent listener called before children...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be fine with something like this if proposed, seems like a good enhancement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The latest React Redux 6 beta is even better. It uses
React.createContext
and only theProvider
subscribes to anything. The connected components receive the latest Redux state through the context and rerender if anything relevant changed. Parent component receives the updated context before children do. Everything is much simpler and it just works.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm interested in exploring further enhancements, and have a few ideas floating in my head related to what's been discussed here. To clarify, do these concerns impact whether this should be merged as-is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. As far as I know, this PR makes things better and doesn't make anything worse 🚢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, great. And thanks for the additional context. While I've done my fair share of scouring
react-redux
source, in how you've laid it out here, there seems to be much we could still do to improve. I'll plan to dig deeper very shortly 🙂