在 FlatList 上使用 React Native 搜索过滤器

IT技术 javascript reactjs react-native
2021-04-10 00:27:40

我正在尝试根据搜索栏文本搜索平面列表。我遇到的问题是,当用户输入错误时......说他们想输入“burger”但错误地输入了“burget”,那么它应该什么都不返回。当用户删除“t”时,它应该使用与“burge”部分匹配的最后一个文本再次重新呈现平面列表。

注意:使用 react-native-elements 搜索栏,它允许我只用 e 或事件调用文本。

到目前为止我在 Main.js 文件中的内容:

searchText = (e) => {
    let text = e.toLowerCase();
    let trucks = this.state.data;

    // search by food truck name
    let filteredName = trucks.filter((truck) => {
      return truck.name.toLowerCase().match(text); 
    });

    // if no match and text is empty
    if(!text || text === '') {
      console.log('change state');
        this.setState({
          data: initial
        });
      }
    // if no name matches to text output
    else if(!Array.isArray(filteredName) && !filteredName.length) {
      console.log("not name");
      this.setState({
        data: [],
      });
    }
    // if name matches then display
    else if(Array.isArray(filteredName)) {
      console.log('Name');
      this.setState({
        data: filteredName,
      });
    }
   };

<View style={styles.container}>
  <SearchBar
    round
    lightTheme
    containerStyle={styles.search}
    ref="search"
    textInputRef="searchText"
    onChangeText={this.searchText.bind(this)}
    placeholder='Search by Truck Name...'
   />
   <TruckList getTruck={(truck) => this.setTruck(truck)} truckScreen={this.truckScreen} data={this.state.data}/>
</View>

然后 TruckList.JS:

export default class TruckList extends Component {
    // rendering truck screen
    renderTruckScreen = (item) => {
        this.props.truckScreen();
        this.props.getTruck(item);
    }

    render() {
        return(
            <List style={styles.list}>
                <FlatList
                    data={this.props.data}
                    renderItem={({ item }) => (
                        <ListItem
                            roundAvatar
                            avatar={{uri: item.pic1}}
                            avatarStyle={styles.avatar}
                            title={item.name}
                            titleStyle={styles.title}
                            subtitle={
                                <View style={styles.subtitleView}>
                                    <Text style={styles.subtitleFood}>{item.food}</Text>
                                    <View style={styles.subtitleInfo}>
                                        <Icon 
                                            name="favorite"
                                            size={20}
                                            color={"#f44336"}
                                            style={styles.subtitleFavorite}
                                        />
                                        <Text style={styles.subtitleFavoriteText}>{item.favorited} favorited</Text>
                                    </View>
                                </View>
                            }
                            onPress={() => this.renderTruckScreen(item)}
                        />
                    )}
                    keyExtractor={(item) => item.uid}
                    ListFooterComponent={this.footer}
                />
            </List>
        )
      }
    }

我尝试了其他几种方法都无济于事。此外,我所见过的为 React Native 工作的唯一解决方案是 ListView,它会随着时间的推移而折旧。所以我试图用新的 FlatList 组件来做到这一点。

谢谢你的帮助!

6个回答

我今天在尝试在新的 FlatList 组件上实现过滤器/搜索功能时遇到了同样的问题。这就是我设法解决它的方法:

通过在父组件的状态中创建另一个名为 noData 的项目,您可以在没有与您的搜索匹配的结果时将其设置为 true,然后有条件地呈现您的 FlatList。

我的实现与你的略有不同,但如果我必须调整你的代码,它看起来像这样:

搜索文本功能:

searchText = (e) => {
    let text = e.toLowerCase()
    let trucks = this.state.data
    let filteredName = trucks.filter((item) => {
      return item.name.toLowerCase().match(text)
    })
    if (!text || text === '') {
      this.setState({
        data: initial
      })
    } else if (!Array.isArray(filteredName) && !filteredName.length) {
      // set no data flag to true so as to render flatlist conditionally
      this.setState({
        noData: true
      })
    } else if (Array.isArray(filteredName)) {
      this.setState({
        noData: false,
        data: filteredName
      })
    }
  }

然后将 noData bool 传递给您的 TruckList 组件:

<TruckList getTruck={(truck) => this.setTruck(truck)} 
truckScreen={this.truckScreen} data={this.state.data} noData={this.state.noData}/>

然后仅在有结果时才在 TruckList 组件中呈现您的 FlatList:

<List style={styles.list}>
{this.props.noData ? <Text>NoData</Text> : <FlatList {...} />}         
</List>

然后应该负责处理用户输入错误 - 因为它会在没有结果时立即重新呈现平面列表,并且在您删除输入错误时会记住以前的搜索状态。

如果有帮助,请告诉我!

你们有这个解决方案的github链接吗?
2021-05-27 00:27:40
如果您控制台记录您正在过滤的数据,您会在那里获得任何数据吗?
2021-05-30 00:27:40
在 item.name.toLowerCase() 它也给出了错误
2021-06-05 00:27:40
this.props.noData真的需要通过吗?只是检查一下不是更干净吗if(this.props.data)
2021-06-12 00:27:40
对我不起作用,当我在做 Trucks.filter((item) => {console.log(item) } 的控制台时,它给出了“消息:“项目未定义””
2021-06-19 00:27:40

对于有用的内存搜索,您应该单独保留初始数据。

我有更简单的解决方案。

此解决方案用于对 FlatList 的数据进行内存搜索,并使用它的 String.prototype .includes() 方法来搜索子字符串。

您可以在此要点中找到该组件的完整源代码; https://gist.github.com/metehansenol/46d065b132dd8916159910d5e9586058

我的初始状态;

this.state = {
  searchText: "",
  data: [],
  filteredData: []
};

我的 SearchBar 组件(它来自 react-native-elements 包);

<SearchBar
  round={true}
  lightTheme={true}
  placeholder="Search..."
  autoCapitalize='none'
  autoCorrect={false}
  onChangeText={this.search}
  value={this.state.searchText}
/>

我的搜索方法;

search = (searchText) => {
  this.setState({searchText: searchText});

  let filteredData = this.state.data.filter(function (item) {
    return item.description.includes(searchText);
  });

  this.setState({filteredData: filteredData});
};

最后是我的 FlatList 的 DataSource 表达式;

<FlatList
  data={this.state.filteredData && this.state.filteredData.length > 0 ? this.state.filteredData : this.state.data}
  keyExtractor={(item) => `item-${item.id}`}
  renderItem={({item}) => <ListItem
    id={item.id}
    code={item.code}
    description={item.description}
  />}
/>

快乐编码...

更新: 这个博客可以帮助你更好地理解在 FlatList 中的搜索。

仅供参考: 如果您有大量的在线数据,那么您也可以使用algolia

我为我调整了上面的代码以使其正常工作。原因是当用户删除最后一个错误字符时,代码会从不包含所有对象的先前搜索列表(状态)中搜索这个新字符串,尽管它必须从可用的完整列表中进行搜索。所以,我现在有两个清单。第一个包含对象的完整列表,第二个只包含呈现的对象列表,这些对象在搜索时会发生变化。

handleSearchInput(e){
    let text = e.toLowerCase()
    let fullList = this.state.fullListData;
    let filteredList = fullList.filter((item) => { // search from a full list, and not from a previous search results list
      if(item.guest.fullname.toLowerCase().match(text))
        return item;
    })
    if (!text || text === '') {
      this.setState({
        renderedListData: fullList,
        noData:false,
      })
    } else if (!filteredList.length) {
     // set no data flag to true so as to render flatlist conditionally
       this.setState({
         noData: true
       })
    }
    else if (Array.isArray(filteredList)) {
      this.setState({
        noData: false,
        renderedListData: filteredList
      })
    }
  }

这是我的解决方案:

您需要备份您的数据

this.state = {
    data: [],
    backup: []
}

关于搜索方法

search = txt => {
    let text = txt.toLowerCase()
    let tracks = this.state.backup
    let filterTracks = tracks.filter(item => {
    if(item.name.toLowerCase().match(text)) {
      return item
    }
  })
  this.setState({ data: filterTracks })
}

说明:当对您的数据调用 setState 时,它​​将更改为当前状态并且无法再次更改。

因此备份数据将处理过滤您的数据。

在 React Native 中为列表视图数据制作搜索栏过滤器

使用搜索栏过滤器在列表视图中实时搜索

  • 我们将从网络调用中加载列表,然后将其显示给用户。
  • 用户可以通过在 TextInput 中输入文本来搜索数据。
  • 插入文本后将调用 SearchFilterFunction 我们将列表数据与插入的数据进行比较,并将创建一个新的数据源。
  • 我们将更新附加到 ListView 的数据源。
  • 它将重新呈现列表,用户将能够看到过滤后的数据。

//This is an example code to Add Search Bar Filter on Listview//
import React, { Component } from 'react';
//import react in our code.
 
import {
  Text,
  StyleSheet,
  View,
  FlatList,
  TextInput,
  ActivityIndicator,
  Alert,
} from 'react-native';
//import all the components we are going to use.
 
export default class App extends Component {
  constructor(props) {
    super(props);
    //setting default state
    this.state = { isLoading: true, text: '' };
    this.arrayholder = [];
  }
 
  componentDidMount() {
    return fetch('https://jsonplaceholder.typicode.com/posts')
      .then(response => response.json())
      .then(responseJson => {
        this.setState(
          {
            isLoading: false,
            dataSource: responseJson
          },
          function() {
            this.arrayholder = responseJson;
          }
        );
      })
      .catch(error => {
        console.error(error);
      });
  }
  SearchFilterFunction(text) {
    //passing the inserted text in textinput
    const newData = this.arrayholder.filter(function(item) {
      //applying filter for the inserted text in search bar
      const itemData = item.title ? item.title.toUpperCase() : ''.toUpperCase();
      const textData = text.toUpperCase();
      return itemData.indexOf(textData) > -1;
    });
    this.setState({
      //setting the filtered newData on datasource
      //After setting the data it will automatically re-render the view
      dataSource: newData,
      text: text,
    });
  }
  ListViewItemSeparator = () => {
    //Item sparator view
    return (
      <View
        style={{
          height: 0.3,
          width: '90%',
          backgroundColor: '#080808',
        }}
      />
    );
  };
  render() {
    if (this.state.isLoading) {
      //Loading View while data is loading
      return (
        <View style={{ flex: 1, paddingTop: 20 }}>
          <ActivityIndicator />
        </View>
      );
    }
    return (
      //ListView to show with textinput used as search bar
      <View style={styles.viewStyle}>
        <TextInput
          style={styles.textInputStyle}
          onChangeText={text => this.SearchFilterFunction(text)}
          value={this.state.text}
          underlineColorAndroid="transparent"
          placeholder="Search Here"
        />
        <FlatList
          data={this.state.dataSource}
          ItemSeparatorComponent={this.ListViewItemSeparator}
          renderItem={({ item }) => (
            <Text style={styles.textStyle}>{item.title}</Text>
          )}
          enableEmptySections={true}
          style={{ marginTop: 10 }}
          keyExtractor={(item, index) => index.toString()}
        />
      </View>
    );
  }
}
const styles = StyleSheet.create({
  viewStyle: {
    justifyContent: 'center',
    flex: 1,
    marginTop: 40,
    padding: 16,
  },
  textStyle: {
    padding: 10,
  },
  textInputStyle: {
    height: 40,
    borderWidth: 1,
    paddingLeft: 10,
    borderColor: '#009688',
    backgroundColor: '#FFFFFF',
  },
});

单击“聆听”了解更多想法