loading...
Cover image for Simple search form in REACT using hooks πŸ”Ž.

Simple search form in REACT using hooks πŸ”Ž.

asimdahall profile image Asim Dahal ・4 min read

Searching is one of the most important components of your web application. Let's take an example of an E-commerce platform where there are thousands of item on sale but to find the specific item you are looking for, you need to search πŸ” for the item using the search component provided by the platform.

Today we will learn to build a simple search form which searches from a list of data using react.

Setting up the project

For setting up your project, you can use either create-react-app or also you can go to CodeSandBox.
You can find an article on setting up your react project here.

After creating the project, at first, let's make a simple UI that has an input field and displays the list of search results.

Go to the index.js file which is at the root of your project and clean up all the code inside and add the following code.

import React from "react";
import ReactDOM from "react-dom";

function App() {
  return (
    <div className="App">
      <input
        type="text"
        placeholder="Search"
      />
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </div>
  );
}

In the component above, we create a simple input form(which currently doesn't do anything) and a mock list of the results that are going to be displayed.

Now we apply two-way data binding to the input field, which basically takes the value from the user and saves it into the state.

import React from "react";
import ReactDOM from "react-dom";

function App() {
 const [searchTerm, setSearchTerm] = React.useState("");
  const handleChange = event => {
    setSearchTerm(event.target.value);
  };
  return (
    <div className="App">
      <input
        type="text"
        placeholder="Search"
        value={searchTerm}
        onChange={handleChange}
      />
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </div>
  );
}

We have now created an state named searchTerm which saves the data from the search input on every occurance of the change event. The handleChange method takes the event object as the arguement and sets the current value of the form to the searchTerm state using setSearchTerm method provided by React.useState method.

Now we create a mock list of data and search the data based on the input provided by the user on the input box we created.

import React from "react";
import ReactDOM from "react-dom";

const people = [
  "Siri",
  "Alexa",
  "Google",
  "Facebook",
  "Twitter",
  "Linkedin",
  "Sinkedin"
];

function App() {
 const [searchTerm, setSearchTerm] = React.useState("");
 const [searchResults, setSearchResults] = React.useState([]);
 const handleChange = event => {
    setSearchTerm(event.target.value);
  };

  return (
    <div className="App">
      <input
        type="text"
        placeholder="Search"
        value={searchTerm}
        onChange={handleChange}
      />
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </div>
  );
}

In the above code snippet, we create a mock list/array named people, from which we are going display the list in our component. We also create a state named searchResults which is used to set the search result.

Now we apply the search functionality to our component.

import React from "react";
import ReactDOM from "react-dom";

const people = [
  "Siri",
  "Alexa",
  "Google",
  "Facebook",
  "Twitter",
  "Linkedin",
  "Sinkedin"
];

function App() {
 const [searchTerm, setSearchTerm] = React.useState("");
 const [searchResults, setSearchResults] = React.useState([]);
 const handleChange = event => {
    setSearchTerm(event.target.value);
  };
 React.useEffect(() => {
    const results = people.filter(person =>
      person.toLowerCase().includes(searchTerm)
    );
    setSearchResults(results);
  }, [searchTerm]);

  return (
    <div className="App">
      <input
        type="text"
        placeholder="Search"
        value={searchTerm}
        onChange={handleChange}
      />
      <ul>
         {searchResults.map(item => (
          <li>{item}</li>
        ))}
      </ul>
    </div>
  );
}

Now in the above code snippet, React.useEffect hook is used which executes whenever the dependency of the method gets changed. The React.useEffect hook takes two arguments. The first argument is the function to execute when the data in the dependency is modified and the second argument is an array of dependencies the React.useEffect hook is dependent on. So whenever the value of the dependencies in the React.useEffect hook changes the function in its first argument executes.

So in the React.useEffect hook above, the dependency is searchTerm which gets changed on every input by the user which in turn executes the function in the first argument of the React.useEffect hook. The following function gets executed

() => {
    const results = people.filter(person =>
      person.toLowerCase().includes(searchTerm.toLowerCase())
    );
    setSearchResults(results);
  }

In the above function, the filter method is applied to the people array which returns a new array according to the condition returned in every iteration. The condition is person.toLowerCase().includes(searchTerm.toLowerCase()) which means if the person in the people's list includes the searchTerm then return true otherwise return false.

After the filtered list is set on the searchResults state using the setSearchResult provided by React.useState hook.

Now we have set the search results to the state, we display it by using the searchResults.map method in our component which iterates over all the searchResults and renders them inside the ul.

  <ul>
     {searchResults.map(item => (
          <li>{item}</li>
     ))}
  </ul>

The final result looks something like this

Result

You can find the completed code here

Thankyou.

You can also follow me on Twitter.

Posted on by:

asimdahall profile

Asim Dahal

@asimdahall

Developer with a special skill of being lazy

Discussion

pic
Editor guide
 

Nice tutorial :) One comment - you don't really need to set the filtered results on the state in this case. I'd just filter the people according to the search term during render:

function App() {
  const [searchTerm, setSearchTerm] = React.useState("");
  const handleChange = event => {
    setSearchTerm(event.target.value);
  };

  const results = !searchTerm
    ? people
    : people.filter(person =>
        person.toLowerCase().includes(searchTerm.toLocaleLowerCase())
      );

  return (
    <div className="App">
      <input
        type="text"
        placeholder="Search"
        value={searchTerm}
        onChange={handleChange}
      />
      <ul>
        {results.map(item => (
          <li>{item}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here's a sandbox.

 

Since setState is asynchronous isn’t it better to use useEffect to handle this side effect? Please explain if I’m wrong here.

 

In this case setState will cause a re-render so the component will be rendered with the new value of filter, which is then applied to the filtered array before displaying the final results.

I still think useEffect is a better approach (readable, maintainable), because calculating search in reality will be a side effect and could be left asynchronous, and not block rendering. When the search calculation is complete, by setting state we can trigger a re-render with the search results instead of blocking rendering, during the typing of a search term.

The functional component itself is the render function, I prefer to leave it as it is and use helper functions to perform side effects. But this is just my opinion, what do you think?

The two approaches are functionally similar. The main difference is that with useEffect approach you introduce extra unnecessary code, plus saving unnecessary values to the state, which can be derived at render time. This is similar to the discussion of storing derived data onto the state.

It's not that useEffect approach is wrong here, it's just that it can be simplified :)

Got it, I'm learning a lot about React from this conversation. Thank you for the link :')

Hi Alex.. Can you make a component in react functional hooks which has sort, filter & search feature using your method?

The final result should be a single element which can be mapped to show the cards accordingly and It should not alter the existing data in the array but re-arrange/show accordingly.

I have a sample data as below:
const data = [
{
_id: "dress1",
image: "/images/fans.jpg",
title: "shirt",
description:
"This is for all the latest trends, no matter who you are, where you’re from and what
you’re up to. Exclusive to ASOS, our universal brand is here for you, and comes in all our fit ranges: ASOS Curve, Tall, Petite and Maternity. Created by us, styled by you.",
availableSizes: ["X", "L", "XL", "XXL"],
price: 29.9584
},
{
_id: "dress2",
image: "/images/mcb.jpg",
title: "Pants",
description:
"This is for all the latest trends, no matter who you are, where you’re from and what you’re up to. Exclusive to ASOS, our universal brand is here for you, and comes in all our fit ranges: ASOS Curve, Tall, Petite and Maternity. Created by us, styled by you.",
availableSizes: ["X", "M", "L"],
price: 18.78
}];

i m facing problem this type data filterting

 

Thank you, that was really helpful! One quastion: do you know how to make the search accept two seach parameters? like i want to search base on the name and also the address?

This what i tried, and failed :(

function App() {
  const [searchTerm, setSearchTerm] = React.useState("");
  const [searchTerm2, setSearchTerm2] = React.useState("");

  const handleChange = event => {
    setSearchTerm(event.target.value);
  };

  const handleChange2 = event => {
    setSearchTerm2(event.target.value);
  };

  const results = !searchTerm || !searchTerm2
    ? people
    : people.filter(person =>
        person.name.toLowerCase().includes(searchTerm.toLocaleLowerCase())
    ||  person.address.toLowerCase().includes(searchTerm2.toLocaleLowerCase())
      );

  return (
    <div className="App">
      <input
        type="text"
        placeholder="Search name"
        value={searchTerm}
        onChange={handleChange}
      />
      <input
        type="text"
        placeholder="Search address"
        value={searchTerm2}
        onChange={handleChange2}
      />
      <ul>
        {results.map(item => (
          <li>{item}</li>
        ))}
      </ul>
    </div>
  );
}
 

I think it'd be const results = !searchTerm && !searchTerm2 instead of || otherwise the filtering will be applied only of both search terms are present.

  const results =
    searchTerm || searchTerm2
      ? !searchTerm2
        ? people.filter(person =>
            person.name.toLowerCase().includes(searchTerm.toLocaleLowerCase())
          )
        : people.filter(
            person =>
              person.address.toLowerCase() ===
              searchTerm2.toLocaleLowerCase()
          )
      : people;

i came up with this solution and it works now but only if one of the conditions is true. I want to make the method filter the two conditions in the same time. So like i want to know all the people that their name is Alex and lives in New york

For your specific case you can do like this:

const results = searchTerm && searchTerm2 ?
  people.filter(person =>
    person.name.toLowerCase().includes(searchTerm.toLocaleLowerCase()) &&
    person.address.toLowerCase().includes(searchTerm2.toLocaleLowerCase())
  ) : people

However, note that the filter won't be applied unless both search terms are present.

Yah but that is exactly what i don't want. I want that the user can choose one of the filters or both of them in the sametime

Hello! In the end you found the solution?

 

when I m try iterate array object data cause some error will you explain why this error and how to solve this
screenshots...
dev-to-uploads.s3.amazonaws.com/i/...

dev-to-uploads.s3.amazonaws.com/i/...

dev-to-uploads.s3.amazonaws.com/i/...

 

Hi, the name property in the first object of the array is a number, which doesn't have string methods such as toLowerCase(). Make sure it is a string.

this is my code link please check ...
my problem is I m using a search filter method to the search box if anybody searches note list
note data search but the problem is if I remove text my search filter my added data old data not
show only filter data show... if you see my code and add some note and filter you will better understand
what I m try to say

link here......
codesandbox.io/s/unruffled-bose-tk...

 

how to use this array instead of people
const searchData = [
{
urlName: "bb",
linkurl: "/bb",
},
{
urlName: "aa",
linkurl: "/aa",
},
{
urlName: "ea",
linkurl: "/ee",
},
{
urlName: "d s",
linkurl: "/dd",
},
];

 

Thanks for sharing your knowledge.

 

how to handle this type functionality and data please reply me I share you screenshots

 

call data from api and filter ....

import React ,{ useState  , useEffect} from 'react';
import axios from 'axios';

export default function Search() {
    const [data ,setData] = useState([]);
    const [filtered ,setFilterd] = useState([]);
    const [result , setResult] = useState("");

    useEffect(()=>{
            const fetchData = async ()=> {
                    try{
                        const res = await axios.get('https://jsonplaceholder.typicode.com/users');
                        setData(res.data);
                        setFilterd(res.data);
                    }catch(err){
                        throw new Error(err);
                    }
                     };
                  fetchData(); 
        },[]);

        useEffect(()=> {
            const results = filtered.filter(res=> res.name.toLowerCase().includes(result)

            ); 
            setData(results)
        } ,[result])
        //console.log(data)

      const onChange =(e)=> {
            setResult(e.target.value);
        }

    return (
        <div>
        <input 
            type="text"
            placeholder="serch here .."
            value={result}
            onChange={onChange}
        />
        {data.map((r,i)=> (   
                <ul key={i}>
                <li>{r.name}</li>
                </ul>)
            )
        }
    </div>
    )  
}

 

codesandbox.io/s/unruffled-bose-tk...

Please Help Me .. See my Code
The problem is when I search in the search field to my note app its work but when I remove search field text my old add data not show only search data show see my code you better understand that what I m trying to say

 

Thanks. I tried you exact code and get this warning:

29:6 warning React Hook useEffect has a missing dependency: 'filtered'. Either include it or remove the dependency array react-hooks/exhaustive-deps

Any suggestions?

My search isn't working

 

Actually, the search is working. I didn't realize that I had to type lower case. Can that be removed? Most people will start typing names in upper case (at least the first letter).

 

Great.
One nitpick here: Make sure you trim your string for searching. Trim removes all the spaces at the start and end of the string.

people.filter(person => person.toLowerCase().includes(searchTerm.trim()));

 

This is great for simple arrays, but what about arrays of objects and you want the search to be broader in scope. For instance if you want to search a fitness class (which is an object) by name, or by duration, or by intensity or any other property

 

The cover photo looks like the macbook has tin worm πŸ›

 

🀣🀣 Actually i got the picture from unsplash

 

Okay well whoever took it, they must have been worried about yin worm 😁

 
 

How do I change the code if I want people to display as a list by default, without typing something in the searchbox?

 

Thanks for this!

 
 

Has anyone built a search app that has a form that the user submits and then display the results?

 

Search only starts with a small letter. It doesn't work with the big one

 

Hi, thank you for this post, it was very helpful. I was wondering, is there a way to make the list hidden, and only show when the user is actually searching?

 

when i try to use this code to filter i m facing this kind of error again and again please help

TypeError: oldData.toLowerCase is not a function

 

Such a detailed post! helped a lot to understand hooks. Much appreciated.

 

It was realy helpful, thanks!
Maybe somebody know how to implement a feature which would highlight the searched results in the list?