2

我正在尝试使用本机反应实现搜索和过滤栏,但不太确定如何使用 DataSource 对象。数据采用 JSON 格式,应执行以下操作:

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

这是关于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);
4

1 回答 1

2

这是解决方案,它在一个功能中进行搜索和过滤。欢迎就如何改进它提出建议。

RNPlay(链接)

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);
于 2016-07-12T16:12:22.787 回答