2

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

  • i reated a State Variable called ==> [inputSearch, setInputSearch]
  • i wrote a textinput inside the ListHeaderComponent of Flatlist

---> 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>
Oussama Nm
  • 35
  • 1
  • 8
  • i dont face this problem when i placed the TextInput outside Flatlist – Oussama Nm Nov 04 '21 at 09:58
  • That's because everytime you call `setInputSearch` the FlatList is re-rendered, and with that is re-rendered the ListHeaderComponent too since you're passing it in an inline function. You should extract the ListHeaderComponent in a standalone component – Giovanni Londero Nov 04 '21 at 11:20
  • i just Searched about "standalone component" and i can't find any Doc about it. What i understand is to declare a new arrow function and place it in "ListHeaderComponent" , i just did it but i the problem didn't solve yet `const HeaderFlatList() => (... Cotent Header )` – Oussama Nm Nov 04 '21 at 12:26
  • and i called it in Flatlist like that : `ListHeaderComponent={HeaderFlatlist}` – Oussama Nm Nov 04 '21 at 12:28

1 Answers1

10

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.

Giovanni Londero
  • 1,309
  • 1
  • 10
  • 20