Skip to content

Commit

Permalink
Merge pull request #134 from software-mansion-labs/poc/freeze-perf
Browse files Browse the repository at this point in the history
Poc/freeze perf
  • Loading branch information
adamgrzybowski authored Nov 8, 2024
2 parents 44bde76 + dab0ad4 commit 4dbdd23
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ index 7558eb3..b7bb75e 100644
}) : STATE_TRANSITIONING_OR_BELOW_TOP;
}
+
+ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Workspace_Initial' || route.name === 'Home' || route.name === 'Search_Bottom_Tab' || route.name === 'Settings_Root') && isScreenActive !== STATE_ON_TOP;
+ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Workspace_Initial' || route.name === 'Home' || route.name === 'Search_Bottom_Tab' || route.name === 'Settings_Root' || route.name === 'ReportsSplitNavigator' || route.name === 'Search_Central_Pane') && isScreenActive !== STATE_ON_TOP;
+
const {
headerShown = true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {NavigationState} from '@react-navigation/native';
import {isFullScreenName} from '@libs/NavigationUtils';

function getIsScreenBlurred(state: NavigationState, currentRouteKey: string) {
const lastFullScreenRoute = state.routes.findLast((route) => isFullScreenName(route.name));
return lastFullScreenRoute?.key !== currentRouteKey;
}

export default getIsScreenBlurred;
28 changes: 28 additions & 0 deletions src/libs/Navigation/AppNavigator/FreezeWrapper/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {useNavigation, useRoute} from '@react-navigation/native';
import React, {useEffect, useState} from 'react';
import {Freeze} from 'react-freeze';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import getIsScreenBlurred from './getIsScreenBlurred';

type FreezeWrapperProps = ChildrenProps & {
/** Prop to disable freeze */
keepVisible?: boolean;
};

function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) {
const navigation = useNavigation();
const currentRoute = useRoute();

const [isScreenBlurred, setIsScreenBlurred] = useState(false);

useEffect(() => {
const unsubscribe = navigation.addListener('state', (e) => setIsScreenBlurred(getIsScreenBlurred(e.data.state, currentRoute.key)));
return () => unsubscribe();
}, [currentRoute.key, navigation]);

return <Freeze freeze={isScreenBlurred && !keepVisible}>{children}</Freeze>;
}

FreezeWrapper.displayName = 'FreezeWrapper';

export default FreezeWrapper;
35 changes: 35 additions & 0 deletions src/libs/Navigation/AppNavigator/FreezeWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {useNavigation, useRoute} from '@react-navigation/native';
import React, {useEffect, useLayoutEffect, useState} from 'react';
import {Freeze} from 'react-freeze';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import getIsScreenBlurred from './getIsScreenBlurred';

type FreezeWrapperProps = ChildrenProps & {
/** Prop to disable freeze */
keepVisible?: boolean;
};

function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) {
const navigation = useNavigation();
const currentRoute = useRoute();

const [isScreenBlurred, setIsScreenBlurred] = useState(false);
const [freezed, setFreezed] = useState(false);

useEffect(() => {
const unsubscribe = navigation.addListener('state', (e) => setIsScreenBlurred(getIsScreenBlurred(e.data.state, currentRoute.key)));
return () => unsubscribe();
}, [currentRoute.key, navigation]);

// Decouple the Suspense render task so it won't be interrupted by React's concurrent mode
// and stuck in an infinite loop
useLayoutEffect(() => {
setFreezed(isScreenBlurred && !keepVisible);
}, [isScreenBlurred, keepVisible]);

return <Freeze freeze={freezed}>{children}</Freeze>;
}

FreezeWrapper.displayName = 'FreezeWrapper';

export default FreezeWrapper;
Original file line number Diff line number Diff line change
@@ -1,42 +1,19 @@
import {useNavigationState, useRoute} from '@react-navigation/native';
import React, {useRef} from 'react';
import {Freeze} from 'react-freeze';
import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import usePermissions from '@hooks/usePermissions';
import memoize from '@libs/memoize';
import createSplitStackNavigator from '@libs/Navigation/AppNavigator/createSplitStackNavigator';
import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper';
import getCurrentUrl from '@libs/Navigation/currentUrl';
import shouldOpenOnAdminRoom from '@libs/Navigation/shouldOpenOnAdminRoom';
import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types';
import {isFullScreenName} from '@libs/NavigationUtils';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import SCREENS from '@src/SCREENS';
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';

function FrozenScreen<TProps extends React.JSX.IntrinsicAttributes>(WrappedComponent: React.ComponentType<TProps>, freeze: boolean) {
return (props: TProps) => (
<Freeze freeze={freeze}>
<WrappedComponent
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
</Freeze>
);
}

function freezeScreenWithLazyLoading(lazyComponent: () => React.ComponentType, freeze: boolean) {
return memoize(
() => {
const Component = lazyComponent();
return FrozenScreen(Component, freeze);
},
{monitoringName: 'freezeScreenWithLazyLoading'},
);
}

const loadReportScreen = () => require<ReactComponentModule>('../../../../pages/home/ReportScreen').default;
const loadSidebarScreen = () => require<ReactComponentModule>('@pages/home/sidebar/SidebarScreen').default;

const Stack = createSplitStackNavigator<ReportsSplitNavigatorParamList>();

Expand All @@ -47,8 +24,8 @@ function ReportsSplitNavigator() {
let initialReportID: string | undefined;
const isInitialRender = useRef(true);

const currentRoute = useRoute();

// TODO: Figure out if compiler affects this code.
// eslint-disable-next-line react-compiler/react-compiler
if (isInitialRender.current) {
const currentURL = getCurrentUrl();
if (currentURL) {
Expand All @@ -60,26 +37,20 @@ function ReportsSplitNavigator() {
initialReportID = initialReport?.reportID ?? '';
}

// eslint-disable-next-line react-compiler/react-compiler
isInitialRender.current = false;
}

const shouldFreeze = useNavigationState((state) => {
const lastFullScreenRoute = state.routes.findLast((route) => isFullScreenName(route.name));
return lastFullScreenRoute?.key !== currentRoute.key;
});

const getSidebarScreen = freezeScreenWithLazyLoading(() => require<ReactComponentModule>('@pages/home/sidebar/SidebarScreen').default, shouldFreeze);

return (
<Freeze freeze={shouldFreeze}>
<FreezeWrapper>
<FocusTrapForScreens>
<Stack.Navigator
sidebarScreen={SCREENS.HOME}
defaultCentralScreen={SCREENS.REPORT}
>
<Stack.Screen
name={SCREENS.HOME}
getComponent={getSidebarScreen}
getComponent={loadSidebarScreen}
/>
<Stack.Screen
name={SCREENS.REPORT}
Expand All @@ -88,7 +59,7 @@ function ReportsSplitNavigator() {
/>
</Stack.Navigator>
</FocusTrapForScreens>
</Freeze>
</FreezeWrapper>
);
}

Expand Down
93 changes: 48 additions & 45 deletions src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar';
import TopBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar';
import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper';
import Navigation from '@libs/Navigation/Navigation';
import type {AuthScreensParamList} from '@libs/Navigation/types';
import * as SearchQueryUtils from '@libs/SearchQueryUtils';
Expand Down Expand Up @@ -58,54 +59,56 @@ function SearchPage({route}: SearchPageProps) {
}

return (
<FullPageNotFoundView
shouldForceFullScreen
shouldShow={!queryJSON}
onBackButtonPress={handleOnBackButtonPress}
shouldShowLink={false}
>
{!!queryJSON && (
<View style={styles.searchSplitContainer}>
<View style={styles.searchSidebar}>
{/* {!selectionMode?.isEnabled && queryJSON ? ( */}
{queryJSON ? (
<View>
<TopBar
activeWorkspaceID={policyID}
breadcrumbLabel={translate('common.search')}
shouldDisplaySearch={false}
<FreezeWrapper>
<FullPageNotFoundView
shouldForceFullScreen
shouldShow={!queryJSON}
onBackButtonPress={handleOnBackButtonPress}
shouldShowLink={false}
>
{!!queryJSON && (
<View style={styles.searchSplitContainer}>
<View style={styles.searchSidebar}>
{/* {!selectionMode?.isEnabled && queryJSON ? ( */}
{queryJSON ? (
<View>
<TopBar
activeWorkspaceID={policyID}
breadcrumbLabel={translate('common.search')}
shouldDisplaySearch={false}
/>
<SearchTypeMenu
queryJSON={queryJSON}
searchName={searchName}
/>
</View>
) : (
<HeaderWithBackButton
title={translate('common.selectMultiple')}
onBackButtonPress={() => {
clearSelectedTransactions();
turnOffMobileSelectionMode();
}}
/>
<SearchTypeMenu
queryJSON={queryJSON}
searchName={searchName}
/>
</View>
) : (
<HeaderWithBackButton
title={translate('common.selectMultiple')}
onBackButtonPress={() => {
clearSelectedTransactions();
turnOffMobileSelectionMode();
}}
)}
<BottomTabBar selectedTab={SCREENS.SEARCH.CENTRAL_PANE} />
</View>
<ScreenWrapper
testID={Search.displayName}
shouldShowOfflineIndicatorInWideScreen
offlineIndicatorStyle={styles.mtAuto}
>
<SearchPageHeader
queryJSON={queryJSON}
hash={queryJSON.hash}
/>
)}
<BottomTabBar selectedTab={SCREENS.SEARCH.CENTRAL_PANE} />
<SearchStatusBar queryJSON={queryJSON} />
<Search queryJSON={queryJSON} />
</ScreenWrapper>
</View>
<ScreenWrapper
testID={Search.displayName}
shouldShowOfflineIndicatorInWideScreen
offlineIndicatorStyle={styles.mtAuto}
>
<SearchPageHeader
queryJSON={queryJSON}
hash={queryJSON.hash}
/>
<SearchStatusBar queryJSON={queryJSON} />
<Search queryJSON={queryJSON} />
</ScreenWrapper>
</View>
)}
</FullPageNotFoundView>
)}
</FullPageNotFoundView>
</FreezeWrapper>
);
}

Expand Down

0 comments on commit 4dbdd23

Please sign in to comment.