File where the data is updated based on the input of the searchbar:
import React, { useCallback, useContext, useEffect, useState } from 'react';
import {
FlatList,
SafeAreaView,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { Path, Svg } from 'react-native-svg';
import { useFocusEffect, useNavigation, useRouter } from 'expo-router';
import * as Haptics from 'expo-haptics';
import toNumber from '../../../utils/toNumber';
import COLORS from '../../../constants/colors';
import { Meal, SearchBar, Selector } from '../../../components';
import { DatabaseContext } from '../../../contexts/DatabaseContext';
import getDate from '../../../utils/getDate';
function AddMeal() {
const db = useContext(DatabaseContext);
const router = useRouter();
const navigation = useNavigation();
const date = getDate();
const [data, setData] = useState(undefined);
const [selection, setSelection] = useState('Food');
useEffect(() => {
navigation.setOptions({
// eslint-disable-next-line react/no-unstable-nested-components
headerLeft: () => (
<TouchableOpacity onPress={() => router.back()}>
<Text style={{ color: COLORS.blue, fontSize: 17 }}>Cancel</Text>
</TouchableOpacity>
),
});
}, [navigation, router]);
useEffect(() => {
db.transaction((tx) => {
tx.executeSql(
`
CREATE TABLE IF NOT EXISTS Meals (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name STRING,
brand STRING,
calories INTEGER,
protein INTEGER,
sugar INTEGER,
fats INTEGER,
portionSize STRING
);
`
);
});
db.transaction((tx) => {
tx.executeSql(
`
CREATE TABLE IF NOT EXISTS Food (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name STRING,
brand STRING,
calories INTEGER,
protein INTEGER,
sugar INTEGER,
fats INTEGER,
portionSize STRING
);
`
);
});
db.transaction((tx) => {
tx.executeSql(
`
CREATE TABLE IF NOT EXISTS Meals${date}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name STRING,
brand STRING,
calories INTEGER,
protein INTEGER,
sugar INTEGER,
fats INTEGER,
portionSize STRING
);
`
);
});
}, [date, db]);
const getSelection = useCallback(() => {
db.transaction((tx) => {
tx.executeSql(
`SELECT * FROM ${selection}`,
null,
(txObj, { rows: { _array } }) => setData(_array)
);
});
}, [db, selection]);
useFocusEffect(
useCallback(() => {
getSelection();
}, [getSelection])
);
useEffect(() => {
getSelection();
}, [getSelection, selection]);
const addData = useCallback(
(meal) => {
db.transaction(
(tx) => (
tx.executeSql(
`
INSERT INTO Meals${date}(
name,
brand,
calories,
protein,
sugar,
fats,
portionSize
) values(?, ?, ?, ?, ?, ?, ?)
`,
[
meal.name,
meal.brand,
toNumber(meal.calories),
toNumber(meal.protein),
toNumber(meal.sugar),
toNumber(meal.fats),
meal.portionSize,
],
null,
(txObj, error) => console.log(error)
// eslint-disable-next-line no-sequences
),
(error) => console.log(error),
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium),
router.back()
)
);
},
[date, db, router]
);
const onChangeText = (text) => {
db.transaction((tx) => {
tx.executeSql(
`
SELECT * FROM ${selection} WHERE name LIKE ?
`,
[`%${text}%`],
(txObj, { rows: { _array } }) => {
setData(_array);
}
);
});
};
const renderItem = useCallback(
({ item }) => (
<TouchableOpacity
onPress={() => {
addData(item);
}}
>
<Meal
id={item.id}
name={item.name}
calories={item.calories}
portionSize={item.portionSize}
selection={selection}
getSelection={getSelection}
/>
</TouchableOpacity>
),
[addData, getSelection, selection]
);
return (
<SafeAreaView
style={{
flex: 1,
backgroundColor: COLORS.background,
}}
>
<View
style={{
flex: 1,
gap: 16,
marginHorizontal: 16,
marginTop: 14,
marginBottom: 16,
}}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
gap: 16,
}}
>
<SearchBar
onChangeText={(text) => {
onChangeText(text);
}}
/>
<TouchableOpacity
onPress={() => router.push('/(tabs)/dashboard/newmeal')}
>
<Svg viewBox="0 0 14 14" width={20} height={20}>
<Path
d="M0.732422 6.26953C0.732422Z"
fill="#0A84FF"
/>
</Svg>
</TouchableOpacity>
</View>
<Selector
setSelection={setSelection}
selection1="Food"
selection2="Meals"
/>
<FlatList
data={data}
style={{ flex: 1 }}
showsVerticalScrollIndicator={false}
renderItem={renderItem}
ItemSeparatorComponent={() => <View style={{ height: 8 }} />}
keyExtractor={(item) => item.id}
/>
</View>
</SafeAreaView>
);
}
export default AddMeal;
searchBar:
import React, { useState } from 'react';
import {
Animated,
Easing,
TextInput,
TouchableHighlight,
TouchableOpacity,
View,
} from 'react-native';
import COLORS from '../../constants/colors';
function SearchBar({ onChangeText }) {
let input;
const [anim] = useState(new Animated.Value(0));
const [width, setWidth] = useState(100);
const AnimatedTouchableHightlight =
Animated.createAnimatedComponent(TouchableHighlight);
return (
<View
onLayout={(event) => {
setWidth(event.nativeEvent.layout.width);
}}
style={{ flexDirection: 'row', flex: 1 }}
>
<AnimatedTouchableHightlight
style={{
paddingVertical: 7,
paddingHorizontal: 8,
backgroundColor: COLORS.box1,
borderRadius: 10,
justifyContent: 'center',
width: anim.interpolate({
inputRange: [0, 1],
outputRange: [width, width - 60],
}),
}}
onPress={() => {
if (input) input.focus();
}}
>
<TextInput
style={{
color: COLORS.text,
marginLeft: 10,
fontSize: 17,
height: 22,
}}
ref={(ref) => {
input = ref;
}}
placeholder="Search"
placeholderTextColor="#98989F"
onFocus={() => {
Animated.timing(anim, {
toValue: 1,
easing: Easing.linear,
duration: 200,
useNativeDriver: false,
}).start();
}}
onBlur={() => {
Animated.timing(anim, {
toValue: 0,
easing: Easing.linear,
duration: 200,
useNativeDriver: false,
}).start();
}}
onChangeText={onChangeText}
/>
</AnimatedTouchableHightlight>
<TouchableOpacity
style={{ justifyContent: 'center' }}
onPress={() => {
if (input) input.blur();
}}
>
<Animated.Text
style={{
color: COLORS.blue,
fontSize: 16,
paddingHorizontal: 7,
opacity: anim,
}}
>
Cancel
</Animated.Text>
</TouchableOpacity>
</View>
);
}
export default SearchBar;
I have been trying a lot of things, mainly changing some functions to use a useCallback hook, but nothing has helped. One thing i did find is that if you remove the setData(_array), and put console.log(_array) in it’s place, the issue does not occur, but ofcourse, the data does not get filtered.




