Skip to content

Commit 06b9eed

Browse files
authored
fix: PortalWhileClosingView relayouting (#3471)
## 🎯 Goal This PR addresses the issue of relayouting `PortalWhileClosingView`s. We remeasure on each layout change so that we know exactly what to set the portal to later on. ## 🛠 Implementation details <!-- Provide a description of the implementation --> ## 🎨 UI Changes <!-- Add relevant screenshots --> <details> <summary>iOS</summary> <table> <thead> <tr> <td>Before</td> <td>After</td> </tr> </thead> <tbody> <tr> <td> <!--<img src="" /> --> </td> <td> <!--<img src="" /> --> </td> </tr> </tbody> </table> </details> <details> <summary>Android</summary> <table> <thead> <tr> <td>Before</td> <td>After</td> </tr> </thead> <tbody> <tr> <td> <!--<img src="" /> --> </td> <td> <!--<img src="" /> --> </td> </tr> </tbody> </table> </details> ## 🧪 Testing <!-- Explain how this change can be tested (or why it can't be tested) --> ## ☑️ Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [ ] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
1 parent f28a769 commit 06b9eed

File tree

1 file changed

+30
-44
lines changed

1 file changed

+30
-44
lines changed

package/src/components/UIComponents/PortalWhileClosingView.tsx

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { ReactNode, useEffect, useRef } from 'react';
2-
import { LayoutChangeEvent, Platform, View } from 'react-native';
2+
import { Platform, View } from 'react-native';
33

4+
import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
45
import { useSafeAreaInsets } from 'react-native-safe-area-context';
56
import { Portal } from 'react-native-teleport';
67

@@ -66,43 +67,38 @@ export const PortalWhileClosingView = ({
6667
}: PortalWhileClosingViewProps) => {
6768
const { closing } = useOverlayController();
6869
const containerRef = useRef<View | null>(null);
69-
const absolutePositionRef = useRef<{ x: number; y: number } | null>(null);
70-
const layoutRef = useRef<{ h: number; w: number }>({ h: 0, w: 0 });
70+
const placeholderLayout = useSharedValue({ h: 0, w: 0 });
7171
const insets = useSafeAreaInsets();
7272

73-
useEffect(() => {
74-
let cancelled = false;
75-
76-
const measureAbsolutePosition = () => {
77-
containerRef.current?.measureInWindow((x, y) => {
78-
if (cancelled) return;
79-
const absolute = {
80-
x,
81-
y: y + (Platform.OS === 'android' ? insets.top : 0),
82-
};
73+
const syncPortalLayout = useStableCallback(() => {
74+
containerRef.current?.measureInWindow((x, y, width, height) => {
75+
const absolute = {
76+
x,
77+
y: y + (Platform.OS === 'android' ? insets.top : 0),
78+
};
8379

84-
absolutePositionRef.current = absolute;
80+
if (!width || !height) {
81+
return;
82+
}
8583

86-
const { h, w } = layoutRef.current;
87-
if (!w || !h) return;
84+
placeholderLayout.value = { h: height, w: width };
8885

89-
setClosingPortalLayout(portalHostName, {
90-
...absolute,
91-
h,
92-
w,
93-
});
86+
setClosingPortalLayout(portalHostName, {
87+
...absolute,
88+
h: height,
89+
w: width,
9490
});
95-
};
91+
});
92+
});
9693

94+
useEffect(() => {
9795
// Measure once after mount and layout settle.
9896
requestAnimationFrame(() => {
99-
requestAnimationFrame(measureAbsolutePosition);
97+
requestAnimationFrame(() => {
98+
syncPortalLayout();
99+
});
100100
});
101-
102-
return () => {
103-
cancelled = true;
104-
};
105-
}, [insets.top, portalHostName]);
101+
}, [insets.top, portalHostName, syncPortalLayout]);
106102

107103
const unregisterPortalHost = useStableCallback(() => clearClosingPortalLayout(portalHostName));
108104

@@ -112,29 +108,19 @@ export const PortalWhileClosingView = ({
112108
};
113109
}, [unregisterPortalHost]);
114110

115-
const onWrapperViewLayout = useStableCallback((event: LayoutChangeEvent) => {
116-
const { height, width } = event.nativeEvent.layout;
117-
layoutRef.current = { h: height, w: width };
118-
119-
const absolute = absolutePositionRef.current;
120-
if (!absolute) return;
121-
122-
setClosingPortalLayout(portalHostName, { ...absolute, h: height, w: width });
123-
});
111+
const placeholderStyle = useAnimatedStyle(() => ({
112+
height: placeholderLayout.value.h,
113+
width: placeholderLayout.value.w > 0 ? placeholderLayout.value.w : '100%',
114+
}));
124115

125116
return (
126117
<>
127118
<Portal hostName={closing ? portalHostName : undefined} name={portalName}>
128-
<View collapsable={false} ref={containerRef} onLayout={onWrapperViewLayout}>
119+
<View collapsable={false} ref={containerRef} onLayout={syncPortalLayout}>
129120
{children}
130121
</View>
131122
</Portal>
132-
{closing && layoutRef.current.h > 0 ? (
133-
<View
134-
pointerEvents='none'
135-
style={{ height: layoutRef.current.h, width: layoutRef.current.w || '100%' }}
136-
/>
137-
) : null}
123+
{closing ? <Animated.View pointerEvents='none' style={placeholderStyle} /> : null}
138124
</>
139125
);
140126
};

0 commit comments

Comments
 (0)