Skip to content

fixes #25 : use onLayout instead of one-shot measureInWindow for reliable dimensions in flex layouts - #26

Open
sherucon wants to merge 1 commit into
phamfoo:mainfrom
sherucon:fix/rect-onlayout-measurement
Open

fixes #25 : use onLayout instead of one-shot measureInWindow for reliable dimensions in flex layouts#26
sherucon wants to merge 1 commit into
phamfoo:mainfrom
sherucon:fix/rect-onlayout-measurement

Conversation

@sherucon

@sherucon sherucon commented Aug 8, 2026

Copy link
Copy Markdown

Replace useLayoutEffect + measureInWindow with onLayout, which is guaranteed to fire with the correct final dimensions after every layout pass:

 function Rect({ children, ...rest }: RectProps) {
   const [rect, setRect] = useState<{ width: number; height: number } | null>(null)
-  const ref = useRef<View>(null)
-
-  useLayoutEffect(() => {
-    if (!isSyncLayoutAccessAvailable()) {
-      throw new Error("This library requires React Native's new architecture.")
-    }
-    ref.current?.measureInWindow((_x, _y, width, height) => {
-      setRect({ width, height })
-    })
-  }, [])
+
+  const handleLayout = React.useCallback(
+    (e: { nativeEvent: { layout: { width: number; height: number } } }) => {
+      const { width, height } = e.nativeEvent.layout
+      setRect((prev) => {
+        if (prev && prev.width === width && prev.height === height) {
+          return prev
+        }
+        return { width, height }
+      })
+    },
+    []
+  )

   return (
-    <View ref={ref} {...rest}>
+    <View onLayout={handleLayout} {...rest}>
       {rect ? children(rect) : null}
     </View>
   )
 }
-
-function isSyncLayoutAccessAvailable() { ... }

What changed

Aspect Before After
Measurement useLayoutEffect([]) + measureInWindow — fires once on mount onLayout — fires on every layout change
Flex compat Can capture intermediate dimensions during flex resolution Always receives final resolved dimensions
Re-measurement Never re-measures after mount Re-measures on any bounds change
Architecture Requires New Architecture (isSyncLayoutAccessAvailable check) Works on both old and new architecture
Re-render guard None Functional setState with equality check avoids unnecessary re-renders

What was removed

  • useRef, useLayoutEffect imports (unused)
  • Platform import (unused)
  • isSyncLayoutAccessAvailable() function (no longer needed)

Note

The onLayout approach also makes the library compatible with the old architecture again, since it doesn't need measureInWindow synchronous access (the isSyncLayoutAccessAvailable guard is removed entirely).

…mensions in flex layouts

The Rect component used useLayoutEffect with an empty dependency array
and measureInWindow to capture its dimensions. This approach has two
problems:

1. It measures exactly once on mount and never re-measures, even if the
   view's bounds change (e.g. screen rotation, dynamic flex layouts).

2. In flex containers, the initial measureInWindow callback can fire
   before flexbox has resolved the final dimensions for all siblings.
   The SVG path is then permanently computed for stale/incorrect
   dimensions.

This is particularly problematic when multiple SquircleView components
share a flex row (e.g. two buttons with flex: 1). The narrower button
may receive an intermediate width measurement that never updates,
causing the squircle background to render at the wrong size — often
appearing visually squashed or mismatched compared to its sibling.

The fix replaces useLayoutEffect+measureInWindow with React Native's
onLayout callback, which fires whenever the view's bounds actually
change. A functional setState with an equality check prevents
unnecessary re-renders when dimensions haven't changed.

This also removes the hard requirement on New Architecture
(isSyncLayoutAccessAvailable), since onLayout works on both the old
and new architecture.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant