-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Simplify useResizeObserver #64820
Simplify useResizeObserver #64820
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
cc @DaniGuardiola in particular who's also been looking at |
The useResizeObserver( element, ( element ) => { ... } ); It starts observing the There's also a const [ size, setSize ] = useState();
useResizeObserver( element, ( element ) => setSite( measure( element ) ) ); Here the Curiously, the If we wanted, I think we could have both hooks: the basic one that fires a callback on resize, and then another one that builds on top of the first one, adding the overlay element and measuring that element specifically. |
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 gave this a look, and it's impressive how much simpler the code is 👏
discovered that it's unnecessarily complex
I'd be curious to know if there were specific use cases when the current implementation was added to the repo (#40509), which caused that implementation to be that complex — maybe @youknowriad has more context around this.
The code changes look, but I assume this would need some good manual testing (js-dom based tests won't pick up any changes, I guess, and I'm not sure how well e2e tests cover this hook).
We could also tentatively merge these changes and assume that there is enough time before the next WordPress release that any blocking bugs that we don't catch while reviewing would still be flagged soon.
} else if ( entry.contentBoxSize[ 0 ] ) { | ||
const contentBoxSize = entry.contentBoxSize[ 0 ]; | ||
entrySize = [ contentBoxSize.inlineSize, contentBoxSize.blockSize ]; | ||
} else { |
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.
Should we make the logic more explicit about checking that entry.contentBoxSize
is an array or an object?
@DaniGuardiola , I don't remember if there was a specific reason why you chose to measure the element yourself, instead of using the measurements from the RO. Using the measumerements from the RO could result in better runtime performance.
Is there ever a scenario in which we wouldn't want the hook to fire on mount? On paper that sounds like a feature that we'd benefit from, regardless.
That is something that we should explore, although not urgent IMO. |
I'll elaborate tomorrow as today I am AFK due to being sick, but in short:
I'll take a closer look tomorrow :) |
Turns out that the answer is no, because
But The spec summarizes it as: |
I added one more improvement: the All examples in the spec and on MDN do a loop like this. Maybe we could look at just the last entry, but I decided to follow the examples and process each entry fully. I think this PR is now ready to be merged. If you don't have any major objections, let's approve and ship it. |
91eac50
to
1e305a9
Compare
Yup, and that is (I believe) one of the main reasons why @DaniGuardiola doesn't use the RO's measurements in the implementation linked above — because we need those measurements to work properly even when transforms are applied.
Code is LGTM, but let's wait for @DaniGuardiola 's last round of feedback, since he mentioned
|
Sorry for not commenting yesterday, but I wanted to finish up a PR I'm about to put up where I've simplified my hook, and I believe it should be possible to conciliate that version with this effort. I'll share more in a bit, once that's ready :) |
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.
Love the cleanup 💯
This appears to work well in my tests 👍
I'm curious if we have any concerns about removing those APIs that useResizeObserver
was supporting but we weren't really using in Gutenberg.
Also, let's wait for @DaniGuardiola's update / review.
@DaniGuardiola @jsnajdr you folks might want to work on this together in a single branch 😉 Why approach this separately?
// behaviour of returning objects instead of arrays for `borderBoxSize` and `contentBoxSize`. | ||
// @ts-ignore | ||
entry[ boxProp ][ sizeType ]; | ||
const [ width, height ] = entrySize.map( ( d ) => Math.round( d ) ); |
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 don't see any use of the custom rounding function, but I assume we'll be fine with always using the default one?
@tyxla to clarify, my upcoming PR does not touch these files, and rather it updates my util along with the Tabs indicator animation (and the upcoming ToggleGroupControl animation which is done and depends on these changes too). The goal is to merge that PR to then compare with what's going on in this PR and see if we can finally unify everything under here. I can't write more details for now (heading to lunch) but the PR is done. Will undraft once I can write more details in its description, but you can look at the refactor I did of the utils for now: #64926 |
Update: PR is ready for review (#64926). |
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.
Looks good, and my needs can be covered in a follow-up PR. Just left a comment about naming and with details about what I want to contribute next.
/> | ||
); | ||
return [ resizeListener, sizes ]; | ||
export default function useResizeObserver(): [ ReactElement, ObservedSize ] { |
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 function is not properly called in an ideal world, it should rather be named useResizeAware
since it's not a direct wrapper over ResizeObserver, but rather a utility that returns an element with specific constraints that is measured, and the resulting measurements.
The unfortunate thing is that the path is named after "useResizeObserver" even though the actual exported utility is "useResizeAware", thanks to the default export which makes things nameless. Then, it is exported as "useResizeObserver" under a named export in the outer barrel file (index.js).
Regarding this utility, a lot of what you could do with a proper, thin wrapper of ResizeObserver can not be achieved with this, like what I do in the animation of Tabs and ToggleGroupControl that I worked in (see latest version in #64926).
However, it should be possible to have everything we need by moving my thin wrapper here, and then using it as a base for this utility. That way, we unlock all use cases instead of only this specific one.
Anyway, as stated above, it seems like the name is irremediably taken, unfortunately. Maybe the lower-level hook (what would be my hook) could be called useObserveElementSize(element, onUpdate, resizeObserverOptions)
.
Update: I added my hook in this PR - #64943
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.
Reading the comments above, it looks like there is agreement on merging these changes, and iterating once Dani's PR is merged
I created a PR that targets this one (so that it can be merged after this one) that clarifies what I intend to add: #64943 ( Then, a final follow-up would be to remove the util from the components package to de-duplicate (see #64926 for the last version of the utility I mean, which is exactly the same as |
Co-authored-by: jsnajdr <[email protected]> Co-authored-by: ciampo <[email protected]> Co-authored-by: tyxla <[email protected]> Co-authored-by: DaniGuardiola <[email protected]>
I've been investigating a bug in WordPress.com where Gutenberg pattern previews trigger a
ResizeObserver
infinite loop:I started by looking at how
useResizeObserver
works, and discovered that it's unnecessarily complex. I decided to remove the unneeded features, which ended up with an almost complete rewrite.useResizeObserver
hook is theuseResizeAware
hook that creates a helper element withResizeObserver
attached to it. This usage doesn't use anyopts
, so they can be removed. Only thecontentBoxSize
entry is ever used, so we don't need to support any other. We also don't use the customround
function.useResolvedElement
and its specialrefOrElement
option is not needed. I'm replacing that with simple logic that mounts theResizeElement
component, stores a local ref to the innerdiv
, and then creates the observer in a layout effect.didUnmountRef
can also be removed, because React 18 no longer complains whensetState
is called after unmount.What do you think?