jerome
jerome

Reputation: 411

How to Create a Search Field in ReactJS

I'm new to react, and I'm working on a small project that uses a search bar to find data that I've gotten from my database.

The code for this component is below:

import React, { Component } from 'react';

class BodyData extends Component {
    state = {
        query: '',
        data: [],
    }

    handleInputChange = () => {
        this.setState({
            query: this.search.value
        })
        this.filterArray();
    }

    getData = () => {
        fetch(`http://localhost:4000/restaurants`)
        .then(response => response.json())
        .then(responseData => {
            // console.log(responseData)
            this.setState({
                data:responseData
            })
        })
    }

    filterArray = () => {
        var searchString = this.state.query;
        var responseData = this.state.data
        if(searchString.length > 0){
            // console.log(responseData[i].name);
            responseData = responseData.filter(l => {
                console.log( l.name.toLowerCase().match(searchString));
            })
        }
    }

    componentWillMount() {
        this.getData();
    }
    render() {
        return (
            <div className="searchForm">
                <form>
                    <input type="text" id="filter" placeholder="Search for..." ref={input => this.search = input} onChange={this.handleInputChange}/>
                </form>
                <div>
                    {
                        this.state.data.map((i) =>
                            <p>{i.name}</p>
                        )
                    }
                </div>
            </div>
        )
    }
}


export default BodyData;

So basically, I want to update the state as I type in the query text, and have the restaurant names I've mapped be reduced till a match is found.

From what I understood, this.state.data will be filtered as I type in my query in the search bar. However when I map out this.state.data, I get the whole list of restaurants instead of what I want to see.

Ive been through a bunch of tutes, and I'm not exactly sure how to go about doing that.

Can anyone help me with this please? Any other comments on the code are also welcome. I'm here to learn :)

Thank you!

Upvotes: 9

Views: 68741

Answers (4)

Pardeep Sharma
Pardeep Sharma

Reputation: 293

Here is the code that will work for you

import React, { Component } from 'react';

class BodyData extends Component {

state = {
    query: '',
    data: [],
    searchString:[]
}

handleInputChange = (event) => {
    this.setState({
        query: event.target.value
    },()=>{
  this.filterArray();
})

}

getData = () => {
    fetch(`http://localhost:4000/restaurants`)
    .then(response => response.json())
    .then(responseData => {
        // console.log(responseData)
        this.setState({
            data:responseData,
            searchString:responseData
        })
    })
}

filterArray = () => {
    let searchString = this.state.query;
    let responseData = this.state.data;



    if(searchString.length > 0){
        // console.log(responseData[i].name);
        responseData = responseData.filter(searchString);
this.setState({
   responseData
})
    }

}

componentWillMount() {
    this.getData();
}
render() {
    return (
        <div className="searchForm">
            <form>
                <input type="text" id="filter" placeholder="Search for..."  onChange={this.handleInputChange}/>
            </form>
            <div>
                {
                    this.state.responseData.map((i) =>
                        <p>{i.name}</p>
                    )
                }
            </div>
        </div>
    )
  }
}


export default BodyData;

There are few changes which is needed.

  1. Set State is worked asynchronous.SO, to avoid it use arrow function when you need to do something immediately after set state.
  2. there are 2 different keyword in es6 instead of var. Let and Const . use them instead of var.
  3. There is no need of ref in input. you can directly get value of input by event.target.value

Enjoy Coding!!

Upvotes: 5

Tholle
Tholle

Reputation: 112787

You could keep an additional piece of state called e.g. filteredData that contains all elements in data that include your query in the name, and then render that.

Example

class BodyData extends React.Component {
  state = {
    query: "",
    data: [],
    filteredData: []
  };

  handleInputChange = event => {
    const query = event.target.value;

    this.setState(prevState => {
      const filteredData = prevState.data.filter(element => {
        return element.name.toLowerCase().includes(query.toLowerCase());
      });

      return {
        query,
        filteredData
      };
    });
  };

  getData = () => {
    fetch(`http://localhost:4000/restaurants`)
      .then(response => response.json())
      .then(data => {
        const { query } = this.state;
        const filteredData = data.filter(element => {
          return element.name.toLowerCase().includes(query.toLowerCase());
        });

        this.setState({
          data,
          filteredData
        });
      });
  };

  componentWillMount() {
    this.getData();
  }

  render() {
    return (
      <div className="searchForm">
        <form>
          <input
            placeholder="Search for..."
            value={this.state.query}
            onChange={this.handleInputChange}
          />
        </form>
        <div>{this.state.filteredData.map(i => <p>{i.name}</p>)}</div>
      </div>
    );
  }
}

Upvotes: 7

Aseem Upadhyay
Aseem Upadhyay

Reputation: 4537

Few pointers I'll like to show-

  1. setState({}) function is asynchronous, you'll have to either use functional setState and call filteredArray method as a callback. Or, call filteredArray at render, which is where your values will be updated and be reflected.
  2. Your filterArray method is not returning / setting the filtered list of data. So what you type, even though it is getting filtered, it is not getting set / returned anywhere.

Upvotes: 0

Pavlo Kozlov
Pavlo Kozlov

Reputation: 1101

setState method is asynchronous and it has second parameter - callback that is called when the state is applied.

You need to change handleInputChange methods.

handleInputChange = () => {
    this.setState({
        query: this.search.value
    }, this.filterArray)
}

Upvotes: 0

Related Questions