From c2cf85b0085498a818179a24ae097087c35ed7a2 Mon Sep 17 00:00:00 2001 From: Shreyansh Singh Date: Sat, 8 Aug 2026 20:01:05 +0530 Subject: [PATCH] fix: use onLayout instead of one-shot measureInWindow for reliable dimensions in flex layouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/index.tsx | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 198b4f5..cb39532 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,11 +1,9 @@ import * as React from 'react' -import { ViewProps, View, StyleSheet, Platform } from 'react-native' +import { ViewProps, View, StyleSheet } from 'react-native' import { PropsWithChildren, ReactNode, useState, - useRef, - useLayoutEffect, } from 'react' import Svg, { Color, Path } from 'react-native-svg' import { getSvgPath } from 'figma-squircle' @@ -144,34 +142,27 @@ function Rect({ children, ...rest }: RectProps) { const [rect, setRect] = useState<{ width: number; height: number } | null>( null ) - const ref = useRef(null) - useLayoutEffect(() => { - if (!isSyncLayoutAccessAvailable()) { - throw new Error("This library requires React Native's new architecture.") - } - - // TODO: Maybe use `getBoundingClientRect` instead when it's stable https://gist.github.com/lunaleaps/148756563999c83220887757f2e549a3#file-tooltip-uselayouteffect-js-L77 - // From my testing, `measureInWindow` is still faster than `unstable_getBoundingClientRect` - 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 ( - + {rect ? children(rect) : null} ) } -function isSyncLayoutAccessAvailable() { - if (Platform.OS === 'web') { - return true - } - - return (globalThis as any).RN$Bridgeless === true -} export { SquircleView, getSvgPath } export type { SquircleParams, SquircleViewProps }