diff --git a/src/BottomSheetScaleView.tsx b/src/BottomSheetScaleView.tsx
index e02bade..5e157af 100644
--- a/src/BottomSheetScaleView.tsx
+++ b/src/BottomSheetScaleView.tsx
@@ -1,7 +1,7 @@
import { type PropsWithChildren } from 'react';
import { StyleSheet } from 'react-native';
import Animated from 'react-native-reanimated';
-import { useScaleAnimatedStyle } from './useScaleAnimation';
+import { useBackgroundScaleAnimatedStyle } from './useScaleAnimation';
/**
* Wraps your app content with iOS-style scale animation when a bottom sheet
@@ -19,7 +19,7 @@ import { useScaleAnimatedStyle } from './useScaleAnimation';
* ```
*/
export function BottomSheetScaleView({ children }: PropsWithChildren) {
- const animatedStyle = useScaleAnimatedStyle();
+ const animatedStyle = useBackgroundScaleAnimatedStyle();
return (
diff --git a/src/QueueItem.tsx b/src/QueueItem.tsx
index 6fa44cc..516f4aa 100644
--- a/src/QueueItem.tsx
+++ b/src/QueueItem.tsx
@@ -1,4 +1,4 @@
-import { useEffect } from 'react';
+import { memo, useEffect } from 'react';
import { StyleSheet, View } from 'react-native';
import Animated from 'react-native-reanimated';
import { useSafeAreaFrame } from 'react-native-safe-area-context';
@@ -15,7 +15,7 @@ import {
} from './bottomSheet.store';
import { BottomSheetBackdrop } from './BottomSheetBackdrop';
import { cleanupSheetRef } from './refsMap';
-import { useScaleAnimatedStyle } from './useScaleAnimation';
+import { useSheetScaleAnimatedStyle } from './useScaleAnimation';
interface QueueItemProps {
id: string;
@@ -23,7 +23,11 @@ interface QueueItemProps {
isActive: boolean;
}
-export function QueueItem({ id, stackIndex, isActive }: QueueItemProps) {
+export const QueueItem = memo(function QueueItem({
+ id,
+ stackIndex,
+ isActive,
+}: QueueItemProps) {
const content = useSheetContent(id);
const usePortal = useSheetUsePortal(id);
const keepMounted = useSheetKeepMounted(id);
@@ -31,7 +35,7 @@ export function QueueItem({ id, stackIndex, isActive }: QueueItemProps) {
const startClosing = useStartClosing();
const { width, height } = useSafeAreaFrame();
- const scaleStyle = useScaleAnimatedStyle({ id });
+ const scaleStyle = useSheetScaleAnimatedStyle(id);
useEffect(() => {
return () => {
@@ -76,7 +80,7 @@ export function QueueItem({ id, stackIndex, isActive }: QueueItemProps) {
>
);
-}
+});
const styles = StyleSheet.create({
container: {},
diff --git a/src/store/hooks.ts b/src/store/hooks.ts
index 2a620c2..34e6308 100644
--- a/src/store/hooks.ts
+++ b/src/store/hooks.ts
@@ -32,6 +32,31 @@ export const useIsSheetOpen = (id: string) =>
return status === 'open' || status === 'opening';
}, shallow);
+export const useHasScaleBackgroundAbove = (id: string) =>
+ useBottomSheetStore((state) => {
+ const { stackOrder, sheetsById } = state;
+ const sheetIndex = stackOrder.indexOf(id);
+
+ if (sheetIndex === -1) {
+ return false;
+ }
+
+ // Check if any sheet above this one has scaleBackground
+ for (let i = sheetIndex + 1; i < stackOrder.length; i++) {
+ const aboveId = stackOrder[i]!;
+ const aboveSheet = sheetsById[aboveId];
+ if (
+ aboveSheet &&
+ aboveSheet.scaleBackground &&
+ aboveSheet.status !== 'closing' &&
+ aboveSheet.status !== 'hidden'
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }, shallow);
+
// Action hooks
export const useOpen = () => useBottomSheetStore((state) => state.open);
diff --git a/src/useScaleAnimation.ts b/src/useScaleAnimation.ts
index 05adf51..9c7c0bf 100644
--- a/src/useScaleAnimation.ts
+++ b/src/useScaleAnimation.ts
@@ -38,7 +38,7 @@ const DEFAULT_CONFIG = {
} satisfies Required;
function useBackgroundScaleDepth(groupId: string): number {
- return useBottomSheetStore((state) => {
+ const depth = useBottomSheetStore((state) => {
const { stackOrder, sheetsById } = state;
for (let i = 0; i < stackOrder.length; i++) {
@@ -55,6 +55,7 @@ function useBackgroundScaleDepth(groupId: string): number {
}
return 0;
});
+ return depth;
}
function useSheetScaleDepth(
@@ -63,7 +64,7 @@ function useSheetScaleDepth(
): number {
const prevDepthRef = useRef(0);
- return useBottomSheetStore((state) => {
+ const result = useBottomSheetStore((state) => {
if (!sheetId) {
return 0;
}
@@ -93,10 +94,11 @@ function useSheetScaleDepth(
prevDepthRef.current = depth;
return depth;
});
+ return result;
}
-export function useScaleAnimatedStyle({ id }: { id?: string } = {}) {
- const { groupId, scaleConfig } = useBottomSheetManagerContext();
+function useScaleAnimatedStyleInternal(scaleDepth: number) {
+ const { scaleConfig } = useBottomSheetManagerContext();
const {
scale = DEFAULT_CONFIG.scale,
@@ -105,14 +107,6 @@ export function useScaleAnimatedStyle({ id }: { id?: string } = {}) {
animation = DEFAULT_CONFIG.animation,
} = scaleConfig ?? {};
- const isBackground = id === undefined;
-
- const backgroundDepth = useBackgroundScaleDepth(groupId);
- const sheetDepth = useSheetScaleDepth(groupId, id);
-
- const scaleDepth = isBackground ? backgroundDepth : sheetDepth;
-
- // Animate the depth change
const progress = useDerivedValue(() => {
if (animation.type === 'spring') {
return withSpring(scaleDepth, animation.config);
@@ -142,3 +136,15 @@ export function useScaleAnimatedStyle({ id }: { id?: string } = {}) {
};
});
}
+
+export function useBackgroundScaleAnimatedStyle() {
+ const { groupId } = useBottomSheetManagerContext();
+ const scaleDepth = useBackgroundScaleDepth(groupId);
+ return useScaleAnimatedStyleInternal(scaleDepth);
+}
+
+export function useSheetScaleAnimatedStyle(sheetId: string) {
+ const { groupId } = useBottomSheetManagerContext();
+ const scaleDepth = useSheetScaleDepth(groupId, sheetId);
+ return useScaleAnimatedStyleInternal(scaleDepth);
+}
diff --git a/src/useSheetRenderData.ts b/src/useSheetRenderData.ts
index 2f2e6f4..d244cca 100644
--- a/src/useSheetRenderData.ts
+++ b/src/useSheetRenderData.ts
@@ -1,4 +1,3 @@
-import { shallow } from 'zustand/shallow';
import {
useBottomSheetStore,
type BottomSheetState,
@@ -11,6 +10,31 @@ export interface SheetRenderItem {
isActive: boolean;
}
+/**
+ * Deep comparison for SheetRenderItem arrays.
+ * Returns true if arrays have same items with same values.
+ */
+function sheetRenderDataEqual(
+ a: SheetRenderItem[],
+ b: SheetRenderItem[]
+): boolean {
+ if (a.length !== b.length) return false;
+
+ for (let i = 0; i < a.length; i++) {
+ const itemA = a[i]!;
+ const itemB = b[i]!;
+ if (
+ itemA.id !== itemB.id ||
+ itemA.stackIndex !== itemB.stackIndex ||
+ itemA.isActive !== itemB.isActive
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
/**
* Returns a flat list of sheets to render.
*
@@ -29,7 +53,7 @@ export function useSheetRenderData(): SheetRenderItem[] {
const active = getActiveSheets(state, groupId);
return [...hiddenPersistent, ...active];
- }, shallow);
+ }, sheetRenderDataEqual);
}
function getHiddenPersistentSheets(