ios – Why does my searchbar get unselected (rerendered) when i enter a single character?


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.

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img