Reputation: 645
I have a FlatList
<View style={styles.container}>
<FlatList data={this.state.restaurants}
renderItem={({ item }) => this.renderItem(item.restaurant)}
keyExtractor={restaurant => restaurant.key}
ListHeaderComponent={() => this.renderHeaderComponent()}
ItemSeparatorComponent={this.renderSeparator}/>
</View>
And have TextInput in header it. I am using TextInput as search bar.
renderHeaderComponent() {
return(
<View style={{ flexDirection: 'row', marginTop: 10, borderBottomColor: '#CED0CE', borderWidth: 1, borderColor: 'transparent' }}>
<Icon name='search' size={30} style={{ marginLeft: 10, marginRight: 10 }}/>
<TextInput
style={{height: 40, flex: 1}}
onChangeText={(text) => this.onChangeText(text)}
placeholder='Type text for search'
clearButtonMode='while-editing'
value={this.state.searchText}
/>
</View>
);
};
In onChangeMethod i filter my data.
onChangeText(text) {
const filteredRestaurants = _.filter(this.props.list, (restaurantObject) => {
const restaurant = restaurantObject.restaurant;
const result = restaurant.name.trim().toLowerCase().includes(text.trim().toLowerCase());
return result;
})
this.setState({
searchText: text,
restaurants: filteredRestaurants
});
}
The problem is following. When I type one symbol in TextInput then focus is lost immediately from TextInput? How can I keep focus in TextInput while typing?
Upvotes: 7
Views: 3305
Reputation: 498
I found another workaround for SectionList
that seems to work so far, and I'll update this answer if I find it stops working. Rather than rendering my component in ListHeaderComponent
I add a dummy section at the start of my data and then use a conditional in renderSectionHeader
to render it out.
<SectionList
sections={[{ title: 'header', data: [] }, ...sections]}
renderSectionHeader={({ section }) =>
section.title === 'header' ? (
<MyListHeaderComponent />
) : (
<DefaultSectionHeaderComponent />
)
}
/>
Having worked with some pretty hairy CollectionView screens in Swift/UIKit it's not that different from how we would handle a similar need in that environment so hopefully that means under the hood perf won't be an issue, but again I'll update this answer if that becomes the case.
Another option may be to just add a dummy item to your sections array so that it never becomes empty but I haven't tried that.
Upvotes: 1
Reputation: 188
I ran into this, and to solve it I wrapped the renderListHeader in a React.useMemo hook and passed the state hook as an item to the dependency array.
renderListHeader = useMemo(() => (
<View style={{ flexDirection: 'row', marginTop: 10, borderBottomColor: '#CED0CE', borderWidth: 1, borderColor: 'transparent' }}>
<Icon name='search' size={30} style={{ marginLeft: 10, marginRight: 10 }}/>
<TextInput
style={{height: 40, flex: 1}}
onChangeText={(text) => this.onChangeText(text)}
placeholder='Type text for search'
clearButtonMode='while-editing'
value={this.state.searchText}
/>
</View>
), [this.onChangeText])
Upvotes: 5
Reputation: 352
This is still an issue for SectionList as of react-native 0.61.5. The auto-bound
method doesn't work since the ListHeaderComponent re-renders when data is becomes an empty array.
I used the following work-around :
Animated.View
Animated.event
to translate Y the Animated.View
Code sample
const animatedScrollYValue = useRef(new Animated.Value(0)).current;
...
<View>
<Animated.View style={{
position: 'absolute',
top: 142,
left: 30,
right: 30,
zIndex: 1,
transform: [{ translateY: Animated.multiply(animatedScrollYValue, new Animated.Value(-1)) }] }}>
// Your text input
</Animated.View>
<Animated.SectionList
scrollEventThrottle={1}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: animatedScrollYValue } } }], { useNativeDriver: true })}
keyExtractor={(item) => item.id}
ListHeaderComponent={// Whatever you want but make you include space for the absolute TextInput}
sections={data}
renderItem={renderItem}
renderSectionHeader={renderHeader}
/>
</View>
Upvotes: 3
Reputation: 3934
You need to use an auto-bound
method for this, as ListHeaderComponent
is of type ReactClass
, and your current method basically re-creates and re-binds its render
every time the data updates, which is not what you want. This concept is further explained in this comment
Anyway, for your example, to fix your issues you should
1) Change your ListHeaderComponent
prop to
ListHeaderComponent={this.renderListHeader}
2) Now you want to change your renderHeaderComponent
method to be an auto-bound method
, and by doing this a new render will not be instantiated every time you change data ( Or enter text into the `TextInput)
renderListHeader = () => (
<View style={{ flexDirection: 'row', marginTop: 10, borderBottomColor: '#CED0CE', borderWidth: 1, borderColor: 'transparent' }}>
<Icon name='search' size={30} style={{ marginLeft: 10, marginRight: 10 }}/>
<TextInput
style={{height: 40, flex: 1}}
onChangeText={(text) => this.onChangeText(text)}
placeholder='Type text for search'
clearButtonMode='while-editing'
value={this.state.searchText}
/>
</View>
)
Upvotes: 8