Skip to content

Commit

Permalink
fix: take preload into account when freezing children (#2566)
Browse files Browse the repository at this point in the history
Fixed #2557. We
have to take preloaded screens into account when freezing screens. Till
now, the assumption was that the newest screens are the ones rendered,
right now top children can be the preloaded ones, so we have to filter
them from calculating the size of frozen screens. We could also think of
freezing the preloaded screens, wdyt about it @satya164 ?
WoLewicki authored Dec 12, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 2152383 commit 6e37c7e
Showing 5 changed files with 23 additions and 30 deletions.
9 changes: 9 additions & 0 deletions apps/src/tests/TestFreeze.tsx
Original file line number Diff line number Diff line change
@@ -38,6 +38,14 @@ function HomeScreen({
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
<Button
title="Preload Details"
onPress={() => navigation.preload('Details')}
/>
<Button
title="Preload Details2"
onPress={() => navigation.preload('Details2')}
/>
</View>
);
}
@@ -84,6 +92,7 @@ function App() {
}}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="Details2" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
7 changes: 6 additions & 1 deletion src/components/Screen.tsx
Original file line number Diff line number Diff line change
@@ -177,6 +177,7 @@ export const InnerScreen = React.forwardRef<View, ScreenProps>(
const {
enabled = screensEnabled(),
freezeOnBlur = freezeEnabled(),
shouldFreeze,
...rest
} = props;

@@ -271,8 +272,12 @@ export const InnerScreen = React.forwardRef<View, ScreenProps>(
}
};

const freeze =
freezeOnBlur &&
(shouldFreeze !== undefined ? shouldFreeze : activityState === 0);

return (
<DelayedFreeze freeze={freezeOnBlur && activityState === 0}>
<DelayedFreeze freeze={freeze}>
<AnimatedScreen
{...props}
/**
30 changes: 1 addition & 29 deletions src/components/ScreenStack.tsx
Original file line number Diff line number Diff line change
@@ -9,19 +9,13 @@ import {
ScreenStackProps,
} from '../types';
import { GHContext, RNSScreensRefContext } from '../contexts';
import { freezeEnabled } from '../core';
import DelayedFreeze from './helpers/DelayedFreeze';
import warnOnce from 'warn-once';

// Native components
import ScreenStackNativeComponent, {
NativeProps,
} from '../fabric/ScreenStackNativeComponent';

function isFabric() {
return 'nativeFabricUIManager' in global;
}

const assertGHProvider = (
ScreenGestureDetector: (
props: PropsWithChildren<GestureProviderProps>,
@@ -69,35 +63,13 @@ function ScreenStack(props: ScreenStackProps) {
passedScreenRefs?.current ?? {},
);
const ref = React.useRef(null);
const size = React.Children.count(children);
const ScreenGestureDetector = React.useContext(GHContext);
const gestureDetectorBridge = React.useRef<GestureDetectorBridge>({
stackUseEffectCallback: _stackRef => {
// this method will be overriden in GestureDetector
},
});

// freezes all screens except the top one
const childrenWithFreeze = React.Children.map(children, (child, index) => {
// @ts-expect-error it's either SceneView in v6 or RouteView in v5
const { props, key } = child;
const descriptor = props?.descriptor ?? props?.descriptors?.[key];
const isFreezeEnabled =
descriptor?.options?.freezeOnBlur ?? freezeEnabled();

// On Fabric, when screen is frozen, animated and reanimated values are not updated
// due to component being unmounted. To avoid this, we don't freeze the previous screen there
const freezePreviousScreen = isFabric()
? size - index > 2
: size - index > 1;

return (
<DelayedFreeze freeze={isFreezeEnabled && freezePreviousScreen}>
{child}
</DelayedFreeze>
);
});

React.useEffect(() => {
gestureDetectorBridge.current.stackUseEffectCallback(ref);
});
@@ -130,7 +102,7 @@ function ScreenStack(props: ScreenStackProps) {
onFinishTransitioning as NativeProps['onFinishTransitioning']
}
ref={ref}>
{childrenWithFreeze}
{children}
</ScreenStackNativeComponent>
</ScreenGestureDetector>
</RNSScreensRefContext.Provider>
3 changes: 3 additions & 0 deletions src/components/ScreenStackItem.tsx
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ function ScreenStackItem(
children,
headerConfig,
activityState,
shouldFreeze,
stackPresentation,
contentStyle,
style,
@@ -132,6 +133,7 @@ function ScreenStackItem(
enabled
isNativeStack
activityState={activityState}
shouldFreeze={shouldFreeze}
stackPresentation={stackPresentation}
hasLargeHeader={headerConfig?.largeTitle ?? false}
style={[style, internalScreenStyle]}
@@ -142,6 +144,7 @@ function ScreenStackItem(
enabled
isNativeStack
activityState={activityState}
shouldFreeze={shouldFreeze}
hasLargeHeader={headerConfig?.largeTitle ?? false}
style={StyleSheet.absoluteFill}>
{content}
4 changes: 4 additions & 0 deletions src/types.tsx
Original file line number Diff line number Diff line change
@@ -102,6 +102,10 @@ export type SearchBarPlacement = 'automatic' | 'inline' | 'stacked';
export interface ScreenProps extends ViewProps {
active?: 0 | 1 | Animated.AnimatedInterpolation<number>;
activityState?: 0 | 1 | 2 | Animated.AnimatedInterpolation<number>;
/**
* Boolean indicating that the screen should be frozen with `react-freeze`.
*/
shouldFreeze?: boolean;
children?: React.ReactNode;
/**
* Boolean indicating that swipe dismissal should trigger animation provided by `stackAnimation`. Defaults to `false`.

0 comments on commit 6e37c7e

Please sign in to comment.