React/Redux: Adding a search feature
emi kojima
Posted on April 20, 2019
The App
Ever since I can remember I've always loved reading. As a kid, I loved going to libraries and bookstores and seeing endless shelves of books. All those options of what I could choose to read felt exciting and expansive. Fast forward to me as an adult walking into a library or bookstore and seeing the same endless shelves of books; now libraries and bookstores are my Burmuda Triangle. As soon as I enter a library or book store, I can't remember any of the books I wanted to read or any of the book recs from my friends. My mind goes B L A N K.
Studies have shown that having too much choice actually prohibits us from making sound informed decisions (which you can learn more about by listening here).
This conundrum is what led me to the creation of my app BookMark (React/Redux, Rails backend, and using the NYT API), where users can peruse the New York Times Bestseller lists by genre, get some information about each book, read the book review, make a note about a book, and click on a book card to save a book to their 'reading list'.
The Search Function
After I created the basics of my app, I wanted to create a search function so that users could search through their saved books by entering a search term.
The structure of React made this seamless because React is component based, which means you can make a Search component and plug that component into where you want the search box to appear.
I started by doing a thorough internet search on how other people were accomplishing searches on their React Apps. After looking at a lot of examples and code, I decided on the best approach to take that made the most sense for my app.
I then started making a list of things I would need to do to make this functionality.
- Create Search component - create a simple search box using
<form>
and<input>
. - Place the Search component into the UserBooks component (where you can see the list of user's books).
- Tie input of the search (the search term) from the Search component to the UserBooks component.
- Create the logic: how do I filter the list of user books according to search term & write the function(s).
- Test to make sure the functionality works.
Step 1. I created a simple presentational Search component that takes in user input. I knew that each time a user typed something in the box, I wanted to trigger my search function. For this, I knew I had to create an onChange event handler so when the time came, I could send that input to my yet-to-be-written search function.
Step 2. I then decided to place the component at the top of my UserBooks component so that the search box appears at the top of that component.
Step 3. Since my Search component is the child component to UserBooks component, I had to somehow figure out a way to pass back the search term to the UserBooks component. I did this by creating a callback() function in the UserBooks component and passing that down to the Search component through props and then setting what I got back to my UserBooks local state.
In my Search component, I used an onChange
event handler and used my callback inside the onChange like this:
const Search = (props) => {
return (
<>
<form>
<input
placeholder="Search for..."
onChange={(event)=>props.callback(event.target.value)}
/>
</form>
</>
)}
and in my UserBooks component:
callback = (term ) => {
this.setState({
term: term
})
}
Now, my search term is connected to my UserBooks component.
Step 4. I knew that in my UserBooks component, I was already displaying all of the user's books, so I need to figure out a way to filter out the books that did not match the user's search term so that only the books that matched remain showing. I also needed to figure out what I wanted the search term to search through (author name, title, book description, user's note about the book).
I decided that I wanted the search to work on all of the book's attributes. I began by drawing out what I wanted to happen.
User's books (with search box) are displayed => search term is entered => some kind of filter function => user books that match the search term remain displayed.
In my UserBooks component, I was already iterating through the list of user's books and passing each book to the UserBookCard component, so I knew this would be the place to put my search filter logic. I started by writing a filter function that would take the book and the search term as arguments.
filterIt = (book, searchTerm) => {
return book.title.includes(searchTerm) || book.author.includes(searchTerm) || book.description.includes(searchTerm)
}
The code above takes a book and checks to see if the book has the characters of your search term. The includes() method returns a boolean depending on whether or not the string contains that characters of your input. If the string contains the characters of your input, the method returns true. In my filterIt() function, I am saying, return the book if any of the book attributes includes the search term. *It's important to note that .includes() is case sensitive, so you will have to .toLowerCase() your input and book attributes.
So now, I have a filterIt() function that returns the book that has the search term within it. NOW, WHAT.
With the list of books I already have and the books that are getting returned by my filterIt() function, I can use the .filter() method which "creates an array filled with all array elements that pass a test (provided as a function)", to get the user's books that contain the search term and then iterate through the filtered list and pass the book on to my UserBookCard component like so:
const booksList = books.filter(this.filterIt(this.state.term)).map(book => {return (<UserBookCard
key={book.id}
book={book}
deleteUserBook={this.props.deleteUserBook}
userId={this.props.user}
addBookNote={this.props.addBookNote} />)
}
)
Step 5. Testing. Debugging. Testing. Debugging. Testing.
The Result
And this is what it looks like in action!
Thanks for reading and happy coding!
Posted on April 20, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.