Salman
Salman

Reputation: 1034

Search input appears empty with ListView

I'm trying to build a listView with a search bar, but it seems that the input value of the search bar doesn't get inserted and tested against available results.

Instead it return an empty text: [ ].

I've even made an empty array { id: 5, text: " ", text2: "test" } and this also doesn't return any results..

I'm now wondering why does my search bar return empty?

The value of the input this.state.searchText does get checked in the for loop against any results.

      for (i = 0; i < textLength; i++) {
    if (aText[i].text === searchText) {
      console.log("found:  " + aText[i].text);
      return aText[i];
    }
  }

I'm still learning, have I approached this wrong? Should I have used an object instead of an array?

And how come I don't get any results?

I've made an example app so you can see the code in an working environment.

Any kind of advice, feedback or criticism is more than welcome.

full example code

import React, { Component } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

import {
  AppRegistry,
  ListView,
  View,
  Text,
  StyleSheet,
  TextInput
} from "react-native";

// Row data (hard-coded)
const rows = [
  { id: 0, text: "View" },
  { id: 1, text: "Text" },
  { id: 2, text: "Image" },
  { id: 3, text: "ScrollView" },
  { id: 4, text: "ListView" },
  { id: 5, text: " ", text2: "test" }
];

// Row comparison function
const rowHasChanged = (r1, r2) => r1.id !== r2.id;

// DataSource template object
const ds = new ListView.DataSource({ rowHasChanged });

export default class App extends Component {
  state = {
    dataSource: ds.cloneWithRows(rows),
    rows
  };

  setSearchText(event) {
    const searchText = event.nativeEvent.text;

    const textLength = this.state.rows.length;
    const aText = this.state.rows;

    const filteredTexts = this.state.rows.filter(checkText);
    console.log("text: " + JSON.stringify(filteredTexts));

    function checkText() {
      var i;
      for (i = 0; i < textLength; i++) {
        if (aText[i].text === searchText) {
          console.log("found:  " + aText[i].text);
          return aText[i];
        }
      }
    }

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

  renderRow = rowData => {
    return (
      <Text style={styles.row}>
        {rowData.text}
        {rowData.text2}
      </Text>
    );
  };

  render() {
    return (
      <View style={styles.SearchBarContainer}>
        <TextInput
          placeholder="Search"
          value={this.state.searchText}
          onChange={this.setSearchText.bind(this)}
          style={styles.searchBar}
          underlineColorAndroid="#DD016B"
          selectionColor="#DD016B"
        />

        <ListView
          style={styles.container}
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  row: {
    padding: 15,
    marginBottom: 5,
    backgroundColor: "skyblue"
  }
});

AppRegistry.registerComponent("App", () => App);

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Upvotes: 1

Views: 152

Answers (3)

Dima Portenko
Dima Portenko

Reputation: 3856

I've suggest to use FlatList since ListView is depricated. I've rewrite your code a bit and you can check it on Expo Snack or just look into code below

import React, { Component } from "react";

import {
  AppRegistry,
  ListView,
  View,
  Text,
  StyleSheet,
  TextInput,
  FlatList
} from "react-native";

// Row data (hard-coded)
const rows = [
  { id: 0, text: "View" },
  { id: 1, text: "Text" },
  { id: 2, text: "Image" },
  { id: 3, text: "ScrollView" },
  { id: 4, text: "ListView" },
  { id: 5, text: " ", text2: "test" }
];


export default class App extends Component {
  state = {
    rows, 
    filteredRows: rows,
  };

  setSearchText(event) {
    const searchText = event.nativeEvent.text;

    const textLength = this.state.rows.length;

    const filteredTexts = this.state.rows.filter(row => {
      return row.text.indexOf(searchText) !== -1;
    });
    console.log("text: " + JSON.stringify(filteredTexts));

    this.setState({
      searchText,
      filteredRows: filteredTexts
    });
  }

  renderRow = rowData => {
    return (
      <Text style={styles.row}>
        {rowData.item.text}
        {rowData.item.text2}
      </Text>
    );
  };

  render() {
    return (
      <View style={styles.SearchBarContainer}>
        <TextInput
          placeholder="Search"
          value={this.state.searchText}
          onChange={this.setSearchText.bind(this)}
          style={styles.searchBar}
          underlineColorAndroid="#DD016B"
          selectionColor="#DD016B"
        />

        <FlatList
          style={styles.container}
          data={this.state.filteredRows}
          renderItem={this.renderRow}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  SearchBarContainer: {
    marginTop: 40,
    flex: 1
  },
  row: {
    padding: 15,
    marginBottom: 5,
    backgroundColor: "skyblue",
    color: 'black',
  },
});

Upvotes: 1

Junaid aziz
Junaid aziz

Reputation: 363

This is very simple you don't need to use for loop to search from the array.

You can get your desired result by simply filter by text match in the object.

I have fixed your example app you can test it.

just replace your looped function with this line const filteredTexts = this.state.rows.filter(record => record.text.toLowerCase().match(searchText));

Upvotes: 2

Naveed Sheriffdeen
Naveed Sheriffdeen

Reputation: 980

You don't seem to have a state to hold the searchText

state = {
    dataSource: ds.cloneWithRows(rows),
    rows,
    searchText:"" //ADD THIS
 };

Upvotes: 2

Related Questions