Skip to content

Commit

Permalink
Merge pull request #51725 from callstack-internal/fix/app-freeze-on-c…
Browse files Browse the repository at this point in the history
…ache-clear

fix: App freeze when clearing cache
  • Loading branch information
tylerkaraszewski authored Nov 7, 2024
2 parents aedbbde + f326c30 commit 7f55e03
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 18 deletions.
42 changes: 25 additions & 17 deletions src/components/OptionListContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import {withOnyx} from 'react-native-onyx';
import type {OnyxCollection} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import usePrevious from '@hooks/usePrevious';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import type {OptionList} from '@libs/OptionsListUtils';
Expand All @@ -17,14 +16,11 @@ type OptionsListContextProps = {
initializeOptions: () => void;
/** Flag to check if the options are initialized */
areOptionsInitialized: boolean;
/** Function to reset the options */
resetOptions: () => void;
};

type OptionsListProviderOnyxProps = {
/** Collection of reports */
reports: OnyxCollection<Report>;
};

type OptionsListProviderProps = OptionsListProviderOnyxProps & {
type OptionsListProviderProps = {
/** Actual content wrapped by this component */
children: React.ReactNode;
};
Expand All @@ -36,6 +32,7 @@ const OptionsListContext = createContext<OptionsListContextProps>({
},
initializeOptions: () => {},
areOptionsInitialized: false,
resetOptions: () => {},
});

const isEqualPersonalDetail = (prevPersonalDetail: PersonalDetails | null, personalDetail: PersonalDetails | null) =>
Expand All @@ -44,12 +41,13 @@ const isEqualPersonalDetail = (prevPersonalDetail: PersonalDetails | null, perso
prevPersonalDetail?.login === personalDetail?.login &&
prevPersonalDetail?.displayName === personalDetail?.displayName;

function OptionsListContextProvider({reports, children}: OptionsListProviderProps) {
function OptionsListContextProvider({children}: OptionsListProviderProps) {
const areOptionsInitialized = useRef(false);
const [options, setOptions] = useState<OptionList>({
reports: [],
personalDetails: [],
});
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);

const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT;
const prevPersonalDetails = usePrevious(personalDetails);
Expand Down Expand Up @@ -144,9 +142,22 @@ function OptionsListContextProvider({reports, children}: OptionsListProviderProp
areOptionsInitialized.current = true;
}, [loadOptions]);

const resetOptions = useCallback(() => {
if (!areOptionsInitialized.current) {
return;
}

areOptionsInitialized.current = false;
setOptions({
reports: [],
personalDetails: [],
});
}, []);

return (
// eslint-disable-next-line react-compiler/react-compiler
<OptionsListContext.Provider value={useMemo(() => ({options, initializeOptions, areOptionsInitialized: areOptionsInitialized.current}), [options, initializeOptions])}>
<OptionsListContext.Provider // eslint-disable-next-line react-compiler/react-compiler
value={useMemo(() => ({options, initializeOptions, areOptionsInitialized: areOptionsInitialized.current, resetOptions}), [options, initializeOptions, resetOptions])}
>
{children}
</OptionsListContext.Provider>
);
Expand All @@ -157,7 +168,7 @@ const useOptionsListContext = () => useContext(OptionsListContext);
// Hook to use the OptionsListContext with an initializer to load the options
const useOptionsList = (options?: {shouldInitialize: boolean}) => {
const {shouldInitialize = true} = options ?? {};
const {initializeOptions, options: optionsList, areOptionsInitialized} = useOptionsListContext();
const {initializeOptions, options: optionsList, areOptionsInitialized, resetOptions} = useOptionsListContext();

useEffect(() => {
if (!shouldInitialize || areOptionsInitialized) {
Expand All @@ -171,13 +182,10 @@ const useOptionsList = (options?: {shouldInitialize: boolean}) => {
initializeOptions,
options: optionsList,
areOptionsInitialized,
resetOptions,
};
};

export default withOnyx<OptionsListProviderProps, OptionsListProviderOnyxProps>({
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
})(OptionsListContextProvider);
export default OptionsListContextProvider;

export {useOptionsListContext, useOptionsList, OptionsListContext};
3 changes: 3 additions & 0 deletions src/pages/settings/Troubleshoot/TroubleshootPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import ImportOnyxState from '@components/ImportOnyxState';
import LottieAnimations from '@components/LottieAnimations';
import MenuItemList from '@components/MenuItemList';
import {useOptionsList} from '@components/OptionListContextProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Section from '@components/Section';
Expand Down Expand Up @@ -51,6 +52,7 @@ function TroubleshootPage() {
const [isLoading, setIsLoading] = useState(false);
const [shouldStoreLogs] = useOnyx(ONYXKEYS.SHOULD_STORE_LOGS);
const [shouldMaskOnyxState = true] = useOnyx(ONYXKEYS.SHOULD_MASK_ONYX_STATE);
const {resetOptions} = useOptionsList({shouldInitialize: false});

const exportOnyxState = useCallback(() => {
ExportOnyxState.readFromOnyxDatabase().then((value: Record<string, unknown>) => {
Expand Down Expand Up @@ -160,6 +162,7 @@ function TroubleshootPage() {
isVisible={isConfirmationModalVisible}
onConfirm={() => {
setIsConfirmationModalVisible(false);
resetOptions();
clearOnyxAndResetApp();
}}
onCancel={() => setIsConfirmationModalVisible(false)}
Expand Down
2 changes: 1 addition & 1 deletion tests/perf-test/SearchRouter.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function SearchRouterInputWrapper() {
function SearchRouterWrapperWithCachedOptions() {
return (
<ComposeProviders components={[OnyxProvider, LocaleContextProvider]}>
<OptionsListContext.Provider value={useMemo(() => ({options: mockedOptions, initializeOptions: () => {}, areOptionsInitialized: true}), [])}>
<OptionsListContext.Provider value={useMemo(() => ({options: mockedOptions, initializeOptions: () => {}, resetOptions: () => {}, areOptionsInitialized: true}), [])}>
<SearchRouter onRouterClose={mockOnClose} />
</OptionsListContext.Provider>
</ComposeProviders>
Expand Down

0 comments on commit 7f55e03

Please sign in to comment.