-
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
@wordpress/data: Introduce useSelect custom hook. #15737
Merged
Merged
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
edeec70
Add useAsyncMode hook
nerrad fbc9f0a
add useRegistry hook
nerrad b698a7e
add new useSelect hook
nerrad a331997
try new `withSelect` implementing useSelect internally.
nerrad dd58983
updates to exports
nerrad d781cfa
updates to README.md file
nerrad 98bf61e
just use useLayoutEffect since GB as a whole doesn’t suppor server si…
nerrad 4ae3461
only make `onStoreChange` dependent on registry and add dependency fo…
nerrad 6aaeb56
latest changes
nerrad a06fa10
implement memoized mapSelect callback.
nerrad 59bb4c3
implement pure on inner component
nerrad c761c54
implement try/catch approach for dealing with zombie props
nerrad 86f864d
implement depdendencies argument
nerrad e891f53
hmm nope, let’s add _mapSelect to useMemo dependencies
nerrad e0aeac2
consider dependencies on whether to regenerate mapOutput
nerrad 78d182b
and… useCallback to the rescue.
nerrad a757297
cover scenarios where state updated _during_ mount before subscriptio…
nerrad 1de3ee6
update with-select tests
nerrad 5565d5c
add new hook tests
nerrad 71984ee
update tests
nerrad c65f45f
code cleanup and add documentation
nerrad cf2e824
update changelog
nerrad 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
cover scenarios where state updated _during_ mount before subscriptio…
…ns are set.
- Loading branch information
commit a757297e3bca23a296b042f65ae14178d585d928
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
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.
I expect this will break the re-rendering order, because the children will subcribe before the parents. Right?
I think the solution might be to
subscribe
synchronously at the render level (but only manage to do it once).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.
hmm...
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.
Yeah, see https://react-redux.js.org/next/api/hooks#stale-props-and-zombie-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.
hmm so the subscription callback would likely have to be a ref maybe?
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.
This basically means for me that subscriptions should happen at the root level of the hook and not inside a
useEffect
.Something like
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.
https://gist.github.com/markerikson/faac6ae4aca7b82a058e13216a7888ec
reduxjs/react-redux#1179
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.
Do you think you'd be able to provide an example (codepen or something) where the React Async rendering would break this. Asking because that would be a good way to try and test alternative implementations addressing this issue.
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.
That would take a while to set up. The entire React API relies on render being idempotent. Note that the current implementation of
withSelect
also has this issue as it has side effects in the constructor.From the React docs:
Conceptually, React does work in two phases:
The commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming async mode (which is not enabled by default yet) breaks the rendering work into pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption).
Render phase lifecycles include the following class component methods:
Because the above methods might be called more than once, it’s important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they can often be non-deterministic.
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods:
For example, consider the following code:
At first glance, this code might not seem problematic. But if
SharedApplicationState.recordEvent
is not idempotent, then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked.By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot.
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.
Thanks for sharing. I think we probably have a lot of components that suffer from these issues. Makes me think we don't really support async mode. Not to say that we shouldn't but if feels like something we should invest in at a more global level.
From what I understand, say we have a global counter, we could increment (side effect) from within hooks. If the render function increments this counter, we'll still be certain that children components will pick a higher-number from this counter than their parent as even if react could double invoke parents render function ... it will still call the render of the children after the parents or is this a wrong assumption (assuming initial rendering)?
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.
Here is the execution order:
You can't run side effects in a parent before running them in its children, because side effects only happen after the first render. This is true without hooks as well. The only alternative I see would be to stagger-render (pause render at each level of the tree to run side effects), but performance would suffer, and you would still have issues when children are rendered conditionally/dynamically.