Skip to content

Commit 73ce167

Browse files
committed
perf(food): implement deferred value updates
1 parent a70195f commit 73ce167

File tree

3 files changed

+55
-16
lines changed

3 files changed

+55
-16
lines changed

src/app/(screens)/food-preferences.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useFoodFilterStore } from '@/hooks/useFoodFilterStore'
1010
import type { FormListSections } from '@/types/components'
1111
import { useRouter } from 'expo-router'
1212
import type React from 'react'
13+
import { startTransition } from 'react'
1314
import { useTranslation } from 'react-i18next'
1415
import { ScrollView, Text, View } from 'react-native'
1516
import { createStyleSheet, useStyles } from 'react-native-unistyles'
@@ -59,6 +60,24 @@ export default function FoodPreferences(): React.JSX.Element {
5960
(state) => state.toggleFoodLanguage
6061
)
6162

63+
const handleToggleRestaurant = (name: string) => {
64+
startTransition(() => {
65+
toggleSelectedRestaurant(name)
66+
})
67+
}
68+
69+
const handleSetShowStatic = (value: boolean) => {
70+
startTransition(() => {
71+
setShowStatic(value)
72+
})
73+
}
74+
75+
const handleToggleLanguage = (language: string) => {
76+
startTransition(() => {
77+
toggleFoodLanguage(language)
78+
})
79+
}
80+
6281
const sections: FormListSections[] = [
6382
{
6483
header: 'Labels',
@@ -86,14 +105,14 @@ export default function FoodPreferences(): React.JSX.Element {
86105
<MultiSectionPicker
87106
elements={elemtents}
88107
selectedItems={selectedRestaurants}
89-
action={toggleSelectedRestaurant}
108+
action={handleToggleRestaurant}
90109
/>
91110
</SectionView>
92111
<SectionView title={'Filter'}>
93112
<SingleSectionPicker
94113
title={t('preferences.formlist.static')}
95114
selectedItem={showStatic ?? false}
96-
action={setShowStatic}
115+
action={handleSetShowStatic}
97116
state={false}
98117
/>
99118
</SectionView>
@@ -104,7 +123,7 @@ export default function FoodPreferences(): React.JSX.Element {
104123
<MultiSectionRadio
105124
elements={languages}
106125
selectedItem={foodLanguage}
107-
action={toggleFoodLanguage}
126+
action={handleToggleLanguage}
108127
/>
109128
</SectionView>
110129
</View>

src/components/Food/FoodScreen.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@ import { pausedToast } from '@/utils/ui-utils'
1212
import { useQuery } from '@tanstack/react-query'
1313
import * as Haptics from 'expo-haptics'
1414
import type React from 'react'
15-
import { memo, useCallback, useEffect, useRef, useState } from 'react'
15+
import {
16+
memo,
17+
useCallback,
18+
useDeferredValue,
19+
useEffect,
20+
useRef,
21+
useState
22+
} from 'react'
1623
import { useTranslation } from 'react-i18next'
1724
import {
1825
Animated,
@@ -39,6 +46,11 @@ function FoodScreen(): React.JSX.Element {
3946
(state) => state.allergenSelection
4047
)
4148

49+
// Use deferredValue for filtering states to prevent UI blocking during expensive updates
50+
const deferredSelectedRestaurants = useDeferredValue(selectedRestaurants)
51+
const deferredShowStatic = useDeferredValue(showStatic)
52+
const deferredAllergenSelection = useDeferredValue(allergenSelection)
53+
4254
const [data, setData] = useState<Food[]>([])
4355
const { t, i18n } = useTranslation('common')
4456
const {
@@ -50,8 +62,9 @@ function FoodScreen(): React.JSX.Element {
5062
isSuccess,
5163
refetch
5264
} = useQuery({
53-
queryKey: ['meals', selectedRestaurants, showStatic],
54-
queryFn: async () => await loadFoodEntries(selectedRestaurants, showStatic),
65+
queryKey: ['meals', deferredSelectedRestaurants, deferredShowStatic],
66+
queryFn: async () =>
67+
await loadFoodEntries(deferredSelectedRestaurants, deferredShowStatic),
5568
staleTime: 1000 * 60 * 0, // 10 minutes
5669
gcTime: 1000 * 60 * 60 * 24 // 24 hours
5770
})
@@ -162,7 +175,8 @@ function FoodScreen(): React.JSX.Element {
162175
const screenHeight = Dimensions.get('window').height
163176
const scrollY = new Animated.Value(0)
164177
const showAllergensBanner =
165-
allergenSelection.length === 1 && allergenSelection[0] === 'not-configured'
178+
deferredAllergenSelection.length === 1 &&
179+
deferredAllergenSelection[0] === 'not-configured'
166180

167181
return (
168182
<SafeAreaProvider>

src/components/Food/MealEntry.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import Color from 'color'
2424
import { LinearGradient } from 'expo-linear-gradient'
2525
import { router } from 'expo-router'
2626
import type React from 'react'
27-
import { memo, useContext, useMemo, useState } from 'react'
27+
import { memo, useContext, useDeferredValue, useMemo, useState } from 'react'
2828
import { useTranslation } from 'react-i18next'
2929
import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'
3030
import { createStyleSheet, useStyles } from 'react-native-unistyles'
@@ -44,27 +44,33 @@ export const MealEntry = memo(
4444
(state) => state.allergenSelection
4545
)
4646
const foodLanguage = useFoodFilterStore((state) => state.foodLanguage)
47+
48+
// Use deferredValue for filter properties to prevent UI blocking
49+
const deferredPreferences = useDeferredValue(preferencesSelection)
50+
const deferredAllergens = useDeferredValue(allergenSelection)
51+
const deferredFoodLanguage = useDeferredValue(foodLanguage)
52+
4753
const { t, i18n } = useTranslation('food')
4854
const { styles, theme } = useStyles(stylesheet)
4955
const userAllergens = useMemo(
5056
() =>
5157
convertRelevantAllergens(
5258
meal.allergens ?? [],
53-
allergenSelection,
59+
deferredAllergens,
5460
i18n.language
5561
),
56-
[meal.allergens, allergenSelection, i18n.language]
62+
[meal.allergens, deferredAllergens, i18n.language]
5763
)
5864
const { userKind = USER_GUEST } =
5965
useContext<UserKindContextType>(UserKindContext)
6066
const userFlags = useMemo(
6167
() =>
6268
convertRelevantFlags(
6369
meal.flags ?? [],
64-
preferencesSelection,
70+
deferredPreferences,
6571
i18n.language
6672
),
67-
[meal.flags, preferencesSelection, i18n.language]
73+
[meal.flags, deferredPreferences, i18n.language]
6874
)
6975
const setSelectedMeal = useRouteParamsStore(
7076
(state) => state.setSelectedMeal
@@ -74,10 +80,10 @@ export const MealEntry = memo(
7480
price !== '' ? getUserSpecificLabel(userKind ?? 'guest', t) : ''
7581

7682
const isNotConfigured =
77-
allergenSelection.length === 1 &&
78-
allergenSelection[0] === 'not-configured'
83+
deferredAllergens.length === 1 &&
84+
deferredAllergens[0] === 'not-configured'
7985
const hasSelectedAllergens =
80-
allergenSelection.length > 0 && !isNotConfigured
86+
deferredAllergens.length > 0 && !isNotConfigured
8187
const hasUserAllergens = userAllergens.length > 0 && !isNotConfigured
8288
const hasNoMealAllergens = hasSelectedAllergens && meal.allergens === null
8389

@@ -157,7 +163,7 @@ export const MealEntry = memo(
157163
<Text style={styles.title} numberOfLines={2}>
158164
{mealName(
159165
meal.name,
160-
foodLanguage,
166+
deferredFoodLanguage,
161167
i18n.language as LanguageKey
162168
)}
163169
</Text>

0 commit comments

Comments
 (0)