Skip to content

Commit 6b62559

Browse files
committed
fix: use layout effect to prevent tearing on Safari
1 parent ceb3671 commit 6b62559

File tree

2 files changed

+15
-10
lines changed

2 files changed

+15
-10
lines changed

src/BottomSheet.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { rubberbandIfOutOfBounds, useDrag } from 'react-use-gesture'
1717
import {
1818
useAriaHider,
1919
useFocusTrap,
20+
useLayoutEffect,
2021
useReady,
2122
useReducedMotion,
2223
useScrollLock,
@@ -143,7 +144,7 @@ export const BottomSheet = React.forwardRef<
143144
const findSnapRef = useRef(findSnap)
144145
const defaultSnapRef = useRef(0)
145146
// Sync the refs with current state, giving the spring full control over when to respond to changes
146-
useEffect(() => {
147+
useLayoutEffect(() => {
147148
maxHeightRef.current = maxHeight
148149
maxSnapRef.current = maxSnap
149150
minSnapRef.current = minSnap
@@ -409,15 +410,15 @@ export const BottomSheet = React.forwardRef<
409410
send('CLOSE')
410411
}
411412
}, [_open, send, ready])
412-
useEffect(() => {
413+
useLayoutEffect(() => {
413414
// Adjust the height whenever the snap points are changed due to resize events
414415
if (maxHeight || maxSnap || minSnap) {
415416
send('RESIZE')
416417
}
417418
}, [maxHeight, maxSnap, minSnap, send])
418419
useEffect(
419420
() => () => {
420-
// Ensure effects are cleaned up on unmount, in case they're not cleaend up otherwise
421+
// Ensure effects are cleaned up on unmount, in case they're not cleaned up otherwise
421422
scrollLockRef.current.deactivate()
422423
focusTrapRef.current.deactivate()
423424
ariaHiderRef.current.deactivate()

src/hooks/useSnapPoints.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { defaultSnapProps, ResizeSource, snapPoints } from '../types'
1111
import { processSnapPoints, roundAndCheckForNaN } from '../utils'
1212
import { useReady } from './useReady'
1313
import { ResizeObserverOptions } from '@juggle/resize-observer/lib/ResizeObserverOptions'
14+
import { useLayoutEffect } from './useLayoutEffect'
1415

1516
export function useSnapPoints({
1617
contentRef,
@@ -187,13 +188,16 @@ function useElementSizeObserver(
187188

188189
useDebugValue(`${label}: ${size}`)
189190

190-
const handleResize = useCallback((entries: ResizeObserverEntry[]) => {
191-
// we only observe one element, so accessing the first entry here is fine
192-
setSize(entries[0].borderBoxSize[0].blockSize)
193-
resizeSourceRef.current = 'element'
194-
}, [])
191+
const handleResize = useCallback(
192+
(entries: ResizeObserverEntry[]) => {
193+
// we only observe one element, so accessing the first entry here is fine
194+
setSize(entries[0].borderBoxSize[0].blockSize)
195+
resizeSourceRef.current = 'element'
196+
},
197+
[resizeSourceRef]
198+
)
195199

196-
useEffect(() => {
200+
useLayoutEffect(() => {
197201
if (!ref.current || !enabled) {
198202
return
199203
}
@@ -232,7 +236,7 @@ function useMaxHeight(
232236
}
233237
}, [ready, setReady])
234238

235-
useEffect(() => {
239+
useLayoutEffect(() => {
236240
// Bail if the max height is a controlled prop
237241
if (controlledMaxHeight) {
238242
setMaxHeight(roundAndCheckForNaN(controlledMaxHeight))

0 commit comments

Comments
 (0)