使用 React Native 进行搜索和过滤

标签 search reactjs react-native filtering react-native-listview

我正在尝试使用 React Native 实现搜索和过滤栏,但不太确定如何使用 DataSource 对象。数据是 JSON 形式,它应该执行以下操作:

  • 基于查询字符串对提供的 JSON 进行全文搜索
  • 基于标签的过滤,使用作为 token 提供的标签

这是 RNPlay (Link) 上的一个非常简单的示例.

如何在React Native中实现搜索和过滤功能?

import React, {Component} from 'react';
import { AppRegistry, View, ListView, Text, TextInput, StyleSheet, TouchableOpacity } from 'react-native';

const FILTERS = [
        {
          tag: "clever", active: false
        }, {
          tag: "scary", active: false
        }, {
          tag: "friendly", active: false
        }, {
          tag: "obedient", active: false
        }
      ];

const FIELDS = [
        {
          title:"Dog",
          subtitle: "Bulldog",
          tags: [ { tag: "clever" }, { tag: "scary" } ]
        }, {
          title:"Cat",
          subtitle:"Persian cat",
          tags: [ { tag: "friendly" }, { tag: "obedient" } ]
        }, {
          title:"Dog",
          subtitle:"Poodle",
          tags: [ { tag: "obedient" } ]
        }
      ];

class SampleApp extends Component {

  constructor(props) {
    super(props);
    var ds = new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
    });
    var ds2 = new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1.active !== row2.active,
    });
    this.state = {
      dataSource: ds.cloneWithRows(FIELDS),
      dataSource2: ds2.cloneWithRows(FILTERS),
      filters: FILTERS,
    };
  }

  renderFilter(filter) {
    return (
        <TouchableOpacity onPress={this.handleClick.bind(this, filter)}>
            <Text style={{fontSize: 24, backgroundColor:(filter.active)?'red':'grey', margin:5}}>{filter.tag}</Text>
      </TouchableOpacity>
    ); 
  }

  renderField(field) {
    return (
      <View style={{flexDirection:'column', borderWidth: 3, borderColor: 'yellow'}}>
        <Text style={{fontSize: 24}}>{field.title}</Text>
        <Text style={{fontSize: 24}}>{field.subtitle}</Text>
        {field.tags.map((tagField) => {
          return (
            <View style={{backgroundColor:'blue'}}>
              <Text style={{fontSize: 24}}>{tagField.tag}</Text>
            </View>
          );
        })}
      </View>
    );
  }

  handleClick(filter) {
    const newFilters = this.state.filters.map(a => {
      let copyA = {...a};
      if (copyA.tag === filter.tag) {
        copyA.active = !filter.active;
      }
      return copyA;
    });
    this.setState({
      dataSource2: this.state.dataSource2.cloneWithRows(newFilters),
      filters: newFilters
    }); 
  }

  setSearchText(event) {
   let searchText = event.nativeEvent.text;
   this.setState({searchText});
  }

  render() {
    return (
      <View>
        <TextInput
                    style={styles.searchBar}
                    value={this.state.searchText}
                    onChange={this.setSearchText.bind(this)}
                    placeholder="Search" />
        <ListView
          style={{flexDirection:'row', flex:1, flexWrap:'wrap'}}
          horizontal={true}
          dataSource={this.state.dataSource2}
          renderRow={this.renderFilter.bind(this)}
        />
        <ListView
          dataSource={this.state.dataSource}
          renderRow={this.renderField.bind(this)}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  searchBar: {
    marginTop: 30,
    fontSize: 40,
    height: 50,
    flex: .1,
    borderWidth: 3,
    borderColor: 'red',
  },
});

AppRegistry.registerComponent('SampleApp', () => SampleApp);

最佳答案

这是解决方案,它在一个函数中同时执行搜索和过滤。欢迎提出有关如何改进它的建议。

RNPlay (Link)

import React, {Component} from 'react';
import { AppRegistry, View, ListView, Text, TextInput, StyleSheet, TouchableOpacity } from 'react-native';

const FILTERS = [
        {
          tag: "clever", active: false
        }, {
          tag: "scary", active: false
        }, {
          tag: "friendly", active: false
        }, {
          tag: "obedient", active: false
        }
      ];

const FIELDS = [
        {
          title:"Dog",
          subtitle: "Bulldog",
          tags: [ "clever", "scary" ],
          active: true,
        }, {
          title:"Cat",
          subtitle:"Persian cat",
          tags: [ "friendly", "obedient" ],
          active: true,
        }, {
          title:"Dog",
          subtitle:"Poodle",
          tags: [ "obedient" ],
          active: true,
        }
      ];

class SampleApp extends Component {

  constructor(props) {
    super(props);
    var ds = new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
    });
    var ds2 = new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1.active !== row2.active,
    });
    this.state = {
      dataSource: ds.cloneWithRows(FIELDS),
      dataSource2: ds2.cloneWithRows(FILTERS),
      filters: FILTERS,
    };
  }

  renderFilter(filter) {
    return (
        <TouchableOpacity onPress={this.handleFilterClick.bind(this, filter)}>
            <Text style={{fontSize: 24, backgroundColor:(filter.active)?'red':'grey', margin:5}}>{filter.tag}</Text>
      </TouchableOpacity>
    ); 
  }

  renderField(field) {

    var fieldElement = <View style={{flexDirection:'column', borderWidth: 3, borderColor: 'yellow'}}>
        <Text style={{fontSize: 24}}>{field.title}</Text>
        <Text style={{fontSize: 24}}>{field.subtitle}</Text>
        {field.tags.map((tagField) => {
          return (
            <View style={{backgroundColor:'blue'}}>
              <Text style={{fontSize: 24}}>{tagField}</Text>
            </View>
          );
        })}
      </View>

    if (field.active == true) {
      return fieldElement;
    } else {
        return null; 
    }
  }

  handleFilterClick(filter) {
    const newFilters = this.state.filters.map(f => {
      let copyF = {...f};
      if (copyF.tag === filter.tag) {
        copyF.active = !filter.active;
      }
      return copyF;
    });
    this.setState({
      dataSource2: this.state.dataSource2.cloneWithRows(newFilters),
      filters: newFilters
    });
    this.searchAndFilter();
  }

  setSearchText(event) {
    let searchText = event.nativeEvent.text;
    this.setState({
      searchText,
    });
    this.searchAndFilter();
  }

  searchAndFilter() {
    //Get filtered tags
    var filteredTags = [];

    this.state.filters.forEach((filter) => {
      if (filter.active) {
        filteredTags.push(filter.tag);
      }
    });

    const searchResults = FIELDS.map(f => {
      let copyF = {...f};

      //Filter
      if (filteredTags.length !== intersect_safe(filteredTags, copyF.tags).length) {
        copyF.active = false;
        return copyF;
      }

      //Search
      if (!this.state.searchText || this.state.searchText == '') {
        copyF.active = true;
      } else if (copyF.title.indexOf(this.state.searchText) != -1) {
        copyF.active = true;
      } else if (copyF.subtitle.indexOf(this.state.searchText) != -1) {
        copyF.active = true;
      } else {
        copyF.active = false;
      }
      return copyF;
    });

    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(searchResults),
    });
  }

  render() {
    return (
      <View>
        <TextInput
                    style={styles.searchBar}
                    value={this.state.searchText}
                    onChange={this.setSearchText.bind(this)}
                    placeholder="Search" />
        <ListView
          style={{flexDirection:'row', flex:1, flexWrap:'wrap'}}
          horizontal={true}
          dataSource={this.state.dataSource2}
          renderRow={this.renderFilter.bind(this)}
        />
        <ListView
          dataSource={this.state.dataSource}
          renderRow={this.renderField.bind(this)}
        />
      </View>
    );
  }
}

function intersect_safe(a, b)
{
  var ai=0, bi=0;
  var result = [];

  while( ai < a.length && bi < b.length )
  {
     if      (a[ai] < b[bi] ){ ai++; }
     else if (a[ai] > b[bi] ){ bi++; }
     else /* they're equal */
     {
       result.push(a[ai]);
       ai++;
       bi++;
     }
  }

  return result;
}

const styles = StyleSheet.create({
  searchBar: {
    marginTop: 30,
    fontSize: 40,
    height: 50,
    flex: .1,
    borderWidth: 3,
    borderColor: 'red',
  },
});

AppRegistry.registerComponent('SampleApp', () => SampleApp);

关于使用 React Native 进行搜索和过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38331260/

相关文章:

vb.net - 搜索 DataGrid 上的所有列

database - 结合地理空间索引的多维搜索

reactjs - React-Slick Carousel - 禁用箭头

javascript - React 包的 CDN 链接以及在使用 React 时如何使用 CDN 中的脚本导入它

ios - 为什么我在尝试扫描 QR 码时在 Expo 中收到此错误?

java - 创建HashMap作为标题关键词的索引,提高搜索效率

php - 搜索庞大的社交数据库

javascript - Google map React 卡在 'loading map...' 中

ios - 某些图像未显示在 react-native-fastimage 中

react-native - 为什么 Metro Bundler 声称 app.json 不存在?