-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
useResizeableDrawer: remove size state storage #88170
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
base: master
Are you sure you want to change the base?
Changes from 2 commits
b450dbe
1d79896
82380c1
5055fef
f54a92f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,5 +1,5 @@ | ||||||
import type {RefObject} from 'react'; | ||||||
import {useCallback} from 'react'; | ||||||
import {useCallback, useState} from 'react'; | ||||||
|
||||||
import {useResizableDrawer} from 'sentry/utils/useResizableDrawer'; | ||||||
import useUrlParams from 'sentry/utils/useUrlParams'; | ||||||
|
@@ -52,12 +52,15 @@ export default function useDetailsSplit({ | |||||
// `initialSize` cannot depend on containerRef because the ref starts as | ||||||
// `undefined` which then gets set into the hook and doesn't update. | ||||||
const initialSize = Math.max(150, window.innerHeight * 0.4); | ||||||
const [containerSize, setContainerSize] = useState(initialSize); | ||||||
|
||||||
const {size: containerSize, ...resizableDrawerProps} = useResizableDrawer({ | ||||||
const resizableDrawerProps = useResizableDrawer({ | ||||||
direction: 'up', | ||||||
initialSize, | ||||||
min: 0, | ||||||
onResize: () => {}, | ||||||
onResize: (newSize: number) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. types can be inferred:
Suggested change
|
||||||
setContainerSize(newSize); | ||||||
}, | ||||||
}); | ||||||
|
||||||
const maxContainerHeight = | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,4 +1,4 @@ | ||||||
import {createContext, useCallback, useMemo} from 'react'; | ||||||
import {createContext, useCallback, useMemo, useState} from 'react'; | ||||||
import styled from '@emotion/styled'; | ||||||
|
||||||
import {IconGrabbable} from 'sentry/icons'; | ||||||
|
@@ -110,17 +110,25 @@ function SplitPanel(props: SplitPanelProps) { | |||||
const min = isLeftRight ? props.left.min : props.top.min; | ||||||
const max = isLeftRight ? props.left.max : props.top.max; | ||||||
|
||||||
const [containerSize, setContainerSize] = useState(() => { | ||||||
const storedSize = sizeStorageKey | ||||||
? parseInt(localStorage.getItem(sizeStorageKey) ?? '', 10) | ||||||
: undefined; | ||||||
return storedSize ?? initialSize; | ||||||
}); | ||||||
|
||||||
const { | ||||||
isHeld, | ||||||
onDoubleClick, | ||||||
onMouseDown: onDragStart, | ||||||
size: containerSize, | ||||||
setSize, | ||||||
} = useResizableDrawer({ | ||||||
direction: isLeftRight ? 'left' : 'down', | ||||||
initialSize, | ||||||
min, | ||||||
onResize: onResize ?? (() => {}), | ||||||
onResize: (size: number) => { | ||||||
setContainerSize(size); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
onResize?.(size); | ||||||
}, | ||||||
sizeStorageKey, | ||||||
}); | ||||||
|
||||||
|
@@ -141,11 +149,11 @@ function SplitPanel(props: SplitPanelProps) { | |||||
() => ({ | ||||||
isMaximized, | ||||||
isMinimized, | ||||||
maximiseSize: () => setSize(max), | ||||||
minimiseSize: () => setSize(min), | ||||||
resetSize: () => setSize(initialSize), | ||||||
maximiseSize: () => setContainerSize(max), | ||||||
minimiseSize: () => setContainerSize(min), | ||||||
resetSize: () => setContainerSize(initialSize), | ||||||
}), | ||||||
[isMaximized, isMinimized, setSize, max, min, initialSize] | ||||||
[isMaximized, isMinimized, setContainerSize, max, min, initialSize] | ||||||
); | ||||||
|
||||||
if (isLeftRight) { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,11 +16,7 @@ export interface UseResizableDrawerOptions { | |
/** | ||
* Triggered while dragging | ||
*/ | ||
onResize: ( | ||
newSize: number, | ||
maybeOldSize: number | undefined, | ||
userEvent: boolean | ||
) => void; | ||
onResize: (size: number, userEvent: boolean) => void; | ||
/** | ||
* The local storage key used to persist the size of the container | ||
*/ | ||
|
@@ -51,32 +47,16 @@ export function useResizableDrawer(options: UseResizableDrawerOptions): { | |
* Call this function to manually set the size of the drawer. | ||
*/ | ||
setSize: (newSize: number, userEvent?: boolean) => void; | ||
/** | ||
* The resulting size of the container axis. Updated while dragging. | ||
* | ||
* NOTE: Be careful using this as this as react state updates are not | ||
* synchronous, you may want to update the element size using onResize instead | ||
*/ | ||
size: number; | ||
} { | ||
const rafIdRef = useRef<number | null>(null); | ||
const currentMouseVectorRaf = useRef<[number, number] | null>(null); | ||
const [size, setSize] = useState<number>(() => { | ||
const storedSize = options.sizeStorageKey | ||
? parseInt(localStorage.getItem(options.sizeStorageKey) ?? '', 10) | ||
: undefined; | ||
|
||
return storedSize || options.initialSize; | ||
}); | ||
const [isHeld, setIsHeld] = useState(false); | ||
const sizeRef = useRef<number>(options.initialSize); | ||
|
||
const updateSize = useCallback( | ||
(newSize: number, userEvent = false) => { | ||
setSize(newSize); | ||
options.onResize(newSize, undefined, userEvent); | ||
if (options.sizeStorageKey) { | ||
localStorage.setItem(options.sizeStorageKey, newSize.toString()); | ||
} | ||
sizeRef.current = newSize; | ||
options.onResize(newSize, userEvent); | ||
}, | ||
[options] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. making a memoized version of something with If we call I guess the idea here was to have memoized versions returned to consumers, and there are consumers that do it right, like My suggestion here would be to use the latest ref pattern to write the options to a ref, then always use the latest version of it inside
This is also what react-query does to the options, because we need them in a This is also where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree. This API is bad and needs to be changed. Curious, but why even use a useLayoutEffect for the latest ref? Couldn't you just write to the ref inside the hook body directly, I don't think that changes anything + you can drop the useLayoutEffect. The hook will be invoked if the options object changes, meaning you'd still get the latest value each time
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it’s technically not allowed to write to refs during render, as that is seen as a side-effect. The react compiler won’t optimize components were it sees those patterns. Technically, react could start a render, you would write to the ref, then it aborts a render (e.g. when using concurrent features). In those cases, the ref would’ve gotten updated “too early”, as the render didn’t complete. so, it’s just against the “rules of react”. |
||
); | ||
|
@@ -85,14 +65,11 @@ export function useResizableDrawer(options: UseResizableDrawerOptions): { | |
// any potentional values set by CSS will be overriden. If no initialDimensions are provided, | ||
// invoke the onResize callback with the previously stored dimensions. | ||
useLayoutEffect(() => { | ||
options.onResize(options.initialSize ?? 0, size, false); | ||
setSize(options.initialSize ?? 0); | ||
sizeRef.current = options.initialSize; | ||
options.onResize(options.initialSize ?? 0, false); | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [options.direction]); | ||
Comment on lines
62
to
63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the only thing we exclude here is |
||
|
||
const sizeRef = useRef<number>(size); | ||
sizeRef.current = size; | ||
|
||
const onMouseMove = useCallback( | ||
(event: MouseEvent) => { | ||
event.stopPropagation(); | ||
|
@@ -138,6 +115,11 @@ export function useResizableDrawer(options: UseResizableDrawerOptions): { | |
); | ||
|
||
const onMouseUp = useCallback(() => { | ||
if (rafIdRef.current !== null) { | ||
window.cancelAnimationFrame(rafIdRef.current); | ||
rafIdRef.current = null; | ||
} | ||
|
||
document.body.style.pointerEvents = ''; | ||
document.body.style.userSelect = ''; | ||
document.documentElement.style.cursor = ''; | ||
|
@@ -161,13 +143,5 @@ export function useResizableDrawer(options: UseResizableDrawerOptions): { | |
updateSize(options.initialSize, true); | ||
}, [updateSize, options.initialSize]); | ||
|
||
useLayoutEffect(() => { | ||
return () => { | ||
if (rafIdRef.current !== null) { | ||
window.cancelAnimationFrame(rafIdRef.current); | ||
} | ||
}; | ||
}); | ||
|
||
return {size, isHeld, onMouseDown, onDoubleClick, setSize: updateSize}; | ||
return {isHeld, onMouseDown, onDoubleClick, setSize: updateSize}; | ||
} |
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.
@ryan953 would you mind linking me to where this component is in the UI? I have verified that this works in profiling, but I would like to check for replay as well
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.
oh, sorry for the huge delay!
this is imported by the replay network details thing. So if you're looking at a replay, click into the Network tab, then click on a specific network row.... you'll get Network Details coming up from the bottom. It's resizable.
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 @ryan953, not a problem about the delay. I have more stuff that I'll need to fix, I just opened a bunch of stacked PRs to get this in, and I'll make sure to test everything where this is used before merging this in