-
Notifications
You must be signed in to change notification settings - Fork 47k
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
DevTools: Scheduling profiler: Add vertical scroll bar #22005
Conversation
46c228f
to
7a7ec56
Compare
@taneliang I have an idea to add support for interactions to bubble (like DOM events) and for views to stop an interaction from bubbling further if they handle it, then composed-scroll-view.mp4 |
@bvaughn That makes sense! I had an initial bubbling implementation but deleted it, probably just because we could do everything we needed without it. Here’s the old code anyway in case there are ideas we can steal! MLH-Fellowship/scheduling-profiler-prototype@46b28d0 |
subviews.forEach(subview => { | ||
// Pass the interaction to subviews first, | ||
// so they have the opportunity to claim it before it bubbles. | ||
for (let i = 0; i < subviews.length; i++) { |
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.
Something I noticed the old code did: I looped through the list of subviews in reverse because later subviews can draw over earlier ones, so we’d want later subviews to handle events first.
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! I could have sworn my bubbling code did the same but I guess that was an earlier iteration that I didn't end up committing. I'll change it to do this though :)
Regarding this issue, I see what the cause is but I'm not sure yet of how to fix it. For example, the code in const subviewDesiredSize = this._subview.desiredSize(); Then we count on the const desiredContentSize = contentLayoutInfo.view.desiredSize(); It's not until the next interaction that the content size has increased– even if I explicitly also call
If I manually trigger |
Okay, I have an idea for an approach that only sucks a little. In const surface = surfaceRef.current;
surface.handleInteraction(interaction);
// Flush any display work that got queued up as part of the previous interaction.
// Typically there should be no work, but certain interactions may need a second pass.
// For example, the ResizableView may collapse/expand its contents,
// which requires a second layout pass for an ancestor VerticalScrollOverflowView.
surface.displayIfNeeded(); |
…positioned at y=0 This layouter computed its remainingHeight value assuming its superview was always positioned at y=0. This caused issues because views are scrolled by changing their frame's origin.y value, as well as all their subviews'.
1. Composes VerticalScrollView for overall scrolling. 2. View accepts optional background color prop. This allowed us to delete lastViewTakesUpRemainingSpaceLayout, which was causing resizing troubles for the composed scroll view component. Initial sizing update delay still remains.
Views are painted first to last, so they should process interactions last to first, so views in front (on top) can claim the interaction first.
This prevents the drag interaction from also scrolling the page
…file. I was getting confused about which class I was looking at while grepping within the file.
…t to scroll vertically This prevents the view from stopping a bubbling interaction that could be handled by an outer view, for example to scroll the outer container.
This is an edge case, but it was causing e.g. the ResizableView's collapse/toggle to not properly update the display/size of the outermost scrollbar.
5dc8f02
to
e00b5cb
Compare
Unfortunately there's another bit of a race case with this item. The outer Adding an async delay before restoring it "works" but is super clunky. I think the interactions between the layout helpers may warrant another pass. Seems like it's awkward to code around them. Maybe I should make them stateful? I don't know. I'm feeling burnt out on this for now. I may merge this as-is and leave those last two items for follow up. |
packages/react-devtools-scheduling-profiler/src/view-base/VerticalScrollView.js
Outdated
Show resolved
Hide resolved
Oh interesting! Did you tried calling Calling
Hmm, this is an unorthodox solution, but would it be a good idea to just not unmount the scheduling profiler when switching tabs? That'll allow us to avoid all of these state restoration issues. I had a similar issue at work where we had a complex component that was extremely expensive to mount, so just keeping it mounted but frozen (with a React.memo hack similar to https://codesandbox.io/s/subtree-freeze-geu6f?file=/src/App.js:925-990) + tucked away somewhere offscreen let us avoid expensive mounts when we tabbed between these components. I think we could do something similar here to preserve component + view state, especially if we'll be facing many state restoration issues/complexity. Just an idea! |
Calling diff --git a/packages/react-devtools-scheduling-profiler/src/CanvasPage.js b/packages/react-devtools-scheduling-profiler/src/CanvasPage.js
index 0caf20a4e3..8c84eac1b1 100644
--- a/packages/react-devtools-scheduling-profiler/src/CanvasPage.js
+++ b/packages/react-devtools-scheduling-profiler/src/CanvasPage.js
@@ -402,12 +402,6 @@ function AutoSizedCanvas({
const surface = surfaceRef.current;
surface.handleInteraction(interaction);
- // Flush any display work that got queued up as part of the previous interaction.
- // Typically there should be no work, but certain interactions may need a second pass.
- // For example, the ResizableView may collapse/expand its contents,
- // which requires a second layout pass for an ancestor VerticalScrollOverflowView.
- surface.displayIfNeeded();
-
canvas.style.cursor = surface.getCurrentCursor() || 'default';
// Defer drawing to canvas until React's commit phase, to avoid drawing
diff --git a/packages/react-devtools-scheduling-profiler/src/view-base/resizable/ResizableView.js b/packages/react-devtools-scheduling-profiler/src/view-base/resizable/ResizableView.js
index b56422f715..fb7a92979d 100644
--- a/packages/react-devtools-scheduling-profiler/src/view-base/resizable/ResizableView.js
+++ b/packages/react-devtools-scheduling-profiler/src/view-base/resizable/ResizableView.js
@@ -79,6 +79,8 @@ export class ResizableView extends View {
}
desiredSize() {
+ this._updateSubviewFrames();
+
const subviewDesiredSize = this._subview.desiredSize();
if (this._shouldRenderResizeBar()) { The easiest way to repro the (broken) behavior locally (with minimal noise from other views) is to remove all subviews from the root except for the The overall flow is:
Oooh, as a side note, we should probably avoid dispatching multiple mouse-move events within an animation frame?
Good call. I'd love to be able to remove the hack if we can find a better way to get the nested layout issue resolved. |
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 think there's lots i need to wrap my head around to give a useful review here, but overall lgtm. added some questions, some seemingly unintended changes are still showing up for me, but accepting to unblock
packages/react-devtools-scheduling-profiler/src/view-base/resizable/ResizeBarView.js
Show resolved
Hide resolved
packages/react-devtools-scheduling-profiler/src/view-base/VerticalScrollOverflowView.js
Show resolved
Hide resolved
Since this has been approved by Luna and Juan, I'm going to go ahead and merge it so we can share with the v18 Working Group and start gathering user feedback. @taneliang Let's continue the discussion thread above with a follow up PR? |
Unfortunately yes, that was what I was suggesting. Interesting that the layouter in step 2 is still using the old size even though It's possible that the
Excited for this! Let's continue this thread in a future PR :) |
Co-authored-by: E-Liang Tan <eliang@eliangtan.com>
Adds a vertical scrollbar to right side when sum of view heights is larger than the canvas.
Remaining items to be done:
This is a pretty big change. May help to look at individual commits?
outer-scroll-bar.mp4