reactjsreact-nativereact-native-flatlistreact-native-textinputlost-focus

Problem Keyboard hide after write one char in TextInput (textinput inside Flatlist Header)


i spent two days on this problem , the problem is :

when i placed a TextInput inside a Flatlist , the behavior of the TextInput Changed , the Keyboard lose focus after every character writed.

Version React-native : 0.63.2 react : 16.13.1, react-dom : 16.13.1, expo : ~42.0.1,

Steps to reproduce

---> The problem is : After every Character pressed in Keyboard the Keyboard Hide

State declaration :

const [inputSearch, setInputSearch] = useState(null);

TextInput is inside Flatlist :

    <View style={styles.container}>
    <StatusBar
         backgroundColor="#11131B"
         barStyle="light-content"
    />

    <View style={{width: '100%', flex: 1 , backgroundColor: 'white', marginTop: 5}}>

        <FlatList 
            contentContainerStyle={{alignItems: 'center',paddingHorizontal: 20}}
            keyExtractor={(item) => (Object.keys(item).length === 0) ? "" : item.id.toString()}
            width= {width}
            numColumns={2}
            ListHeaderComponent={() =>
                <View  style={{flexDirection:'column',justifyContent:'space-between'}}>

                   <Text style={{color:Colors.dark1, fontSize:14, marginBottom: 5}}>Search :</Text>
                   <TextInput style={styles.input} value={inputSearch} onChangeText={setInputSearch} placeholder="Username"/>

                </View>
            }
            data={SIMPLE_DATA}
            renderItem={renderItemRow}
        >

        </FlatList>
    </View>

  <View>

Solution

  • I'm going to expand what I wrote on the comment since that was written quickly and not clear enough.

    Let's consider the following as a starting point (I stripped off useless information):

    function YourComponent() {
      const [inputSearch, setInputSearch] = useState('');
    
      return (
        <View>
          <StatusBar backgroundColor="#11131B" barStyle="light-content" />
    
          <View>
            <FlatList
              ListHeaderComponent={() => (
                <>
                  <Text>Search :</Text>
                  <TextInput
                    value={inputSearch}
                    onChangeText={setInputSearch}
                    placeholder="Username"
                  />
                </>
              )}
              data={[]}
              renderItem={() => <View />}
            />
          </View>
        </View>
      );
    }
    

    Every time you call setInputSearch the component is rendered again, but this means that the arrow function you are passing inline inside ListHeaderComponent will be called every time and the content will be created again (the keyboard is closed because the input where the focus was on doesn't exist anymore, since it's been replaced by a new one).

    How can you solve this? ListHeaderComponent accepts both constructors and ReactElement, so you either pass a ReactElement or create a "standalone" component (no specific meaning, it's just a component on its own, not inside another component).


    Here are a couple of solutions:

    1. ReactElement example (notice that the component is passed directly, there is no function in ListHeaderComponent; you can also extract this as another component if you want):
    function YourComponent() {
      const [inputSearch, setInputSearch] = useState('');
    
      return (
        <View>
          <StatusBar backgroundColor="#11131B" barStyle="light-content" />
    
          <View>
            <FlatList
              ListHeaderComponent={
                <>
                  <Text>Search :</Text>
                  <TextInput
                    value={inputSearch}
                    onChangeText={setInputSearch}
                    placeholder="Username"
                  />
                </>
              }
              data={[]}
              renderItem={() => <View />}
            />
          </View>
        </View>
      );
    }
    
    1. Standalone component (passing the constructor to ListHeaderComponent):
    function YourComponent() {
      return (
        <View>
          <StatusBar backgroundColor="#11131B" barStyle="light-content" />
    
          <View>
            <FlatList
              ListHeaderComponent={ListHeaderComponent}
              data={[]}
              renderItem={() => <View />}
            />
          </View>
        </View>
      );
    }
    
    function ListHeaderComponent() {
      const [inputSearch, setInputSearch] = useState('');
    
      return (
        <>
          <Text>Search :</Text>
          <TextInput
            value={inputSearch}
            onChangeText={setInputSearch}
            placeholder="Username"
          />
        </>
      );
    }
    

    but this wouldn't allow you to use the inputSearch value in YourComponent.


    I hope this helps clear things out.