我有一个平面列表
<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>
并在标题中输入文本。我正在使用文本输入作为搜索栏。
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>
);
};
在onChangeMethod中,我过滤了我的数据。
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
});
}
问题如下。当我在文本输入中键入一个符号时,文本输入立即失去焦点?如何在键入时在文本输入中保持焦点?
为此使用 auto-bound
方法,因为ListHeaderComponent
属于 ReactClass
类型,并且您当前的方法基本上每次数据更新时都会重新创建并重新绑定其render
,这不是您想要的。这个概念在本评论中进一步解释
无论如何,对于您的示例,要解决您的问题,您应该
1)将ListHeaderComponent
道具更改为
ListHeaderComponent={this.renderListHeader}
2)现在你想将你的renderHeaderComponent
方法更改为auto-bound method
,通过这样做,每次更改数据时都不会实例化新的渲染(或在"文本输入"中输入文本)
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>
)
我遇到了这个问题,为了解决这个问题,我将 renderListHeader 包装在 React.useMemo 钩子中,并将状态钩子作为项目传递给依赖数组。
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])
对于 react-native 0.61.5 的 SectionList 来说,这仍然是一个问题。auto-bound
方法不起作用,因为当数据变为空数组时,ListHeaderComponent 会重新呈现。
我使用了以下解决方法:
- 将文本输入代码移动到与节列表相同的级别
- 使用绝对位置,将其放置在所需的位置。
- 用
Animated.View
包裹 - 利用
Animated.event
将 Y 转换为Animated.View
代码示例
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>
我找到了另一种到目前为止似乎有效的SectionList
解决方法,如果我发现它停止工作,我会更新这个答案。 我没有在ListHeaderComponent
中渲染我的组件,而是在数据的开头添加一个虚拟部分,然后使用条件 in renderSectionHeader
将其呈现出来。
<SectionList
sections={[{ title: 'header', data: [] }, ...sections]}
renderSectionHeader={({ section }) =>
section.title === 'header' ? (
<MyListHeaderComponent />
) : (
<DefaultSectionHeaderComponent />
)
}
/>
在 Swift/UIKit 中使用了一些非常毛茸茸的 CollectionView 屏幕后,它与我们处理该环境中类似需求的方式没有什么不同,所以希望这意味着引擎盖下的性能不会成为问题,但如果发生这种情况,我会再次更新这个答案。
另一种选择可能是将虚拟项目添加到您的 sections 数组中,这样它就不会变空,但我还没有尝试过。