搜索时输入一个字符后,TextInput失去焦点

7
我有一个 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>

在头部放置TextInput,我将其用作搜索栏。

 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
    });
  }

问题如下。当我在TextInput中输入一个符号时,焦点立即从TextInput中丢失?我如何在输入时保持TextInput中的焦点?

4个回答

8
您需要使用auto-bound方法来实现此功能,因为ListHeaderComponentReactClass类型的,而您当前的方法基本上每次数据更新时都会重新创建和重新绑定其render,这不是您想要的。这个概念在这个评论中有进一步解释。
无论如何,在您的示例中,要解决问题,您应该:
1)将您的ListHeaderComponent属性更改为 ListHeaderComponent={this.renderListHeader} 2)现在,您需要将您的renderHeaderComponent方法更改为auto-bound方法,通过这样做,每次更改数据(或在`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>
)

1
我有完全相同的问题,但(仅在Android上),这个解决方案不幸地没有帮助... - Matthias Samsel

5

我遇到了这个问题,解决方法是把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])

3
React Native 0.61.5版本中,SectionList仍存在这个问题。当数据变为空数组时,auto-bound方法无法正常工作,因为ListHeaderComponent会重新渲染。以下是我使用的解决方法:
  1. 将文本输入代码移动到与部分列表相同的级别
  2. 使用绝对定位,在所需位置放置它。
  3. 将其包装在Animated.View
  4. 利用Animated.eventAnimated.View向下平移Y
代码示例:
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>

0
我找到了另一种似乎到目前为止有效的SectionList解决方法,如果我发现它不再起作用,我将更新这个答案。我不是在ListHeaderComponent中渲染我的组件,而是在我的数据开头添加一个虚拟部分,然后在renderSectionHeader中使用一个条件语句来将其呈现出来。
<SectionList
  sections={[{ title: 'header', data: [] }, ...sections]}
  renderSectionHeader={({ section }) => 
    section.title === 'header' ? (
      <MyListHeaderComponent />
    ) : (
      <DefaultSectionHeaderComponent />
    )
  }
/>

在使用Swift/UIKit处理一些相当复杂的CollectionView屏幕时,与我们在该环境中处理类似需求的方式并没有太大区别,因此希望这意味着底层性能不会成为问题,但如果情况发生变化,我会更新这个答案。

另一个选择可能是只向您的部分数组添加一个虚拟项,以便它永远不会变为空,但我还没有尝试过。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接