skip to Main Content

What I want to do is create a Search functionality in my rudimentary React phonebook app. The initial data for the persons is stored in a db.json file in the project root.

This is the problematic code where after I type in something within an input field, the whole display for the list of persons goes blank:

import React, {useState} from 'react';

const Search = ({persons, setPersons}) => {
    const [filter, setFilter] = useState('');

    const handleFilterEntry = (event) => {
        const value = event.target.value;
        setFilter(value);
        const filteredPersons = persons.filter( person => {
            person.name.toLowerCase().includes(value.toLowerCase())
        });
        setPersons(filteredPersons);
        console.log(value);
    }
    return (
        <div>
            Filter names with: <input value = {filter} onChange={handleFilterEntry}/>
        </div>
    )
}

export default Search;

And this is the updated code for the handleFilterEntry function that fixes the problem:

const handleFilterEntry = (event) => {
        const value = event.target.value;
        setFilter(value);

        const filteredPersons = value
            ? persons.filter(
                (person) =>
                    person.name.toLowerCase().includes(value.toLowerCase())
            )
            : persons;

        setPersons(filteredPersons);
        console.log(value);
    };

I want to know why adding that conditional logic worked and solved the problem?

I also want to know that on backspacing certain characters from the input field, why does the list not revert back to show list of Persons with the backspaced letters.

Example:
There are names Joe and Jack in the list.
I type J, it filters down to Joe and Jack.
I type Jo, it filters down to Joe.
But I backspace to just J, it does not revert back to display both Joe and Jack in the list and still displays just Joe.

Any help would be appreciated, I’m a newbie. Thanks in advance!

2

Answers


  1. part 1. Why does it show an empty list

    You are not returning the check value in your filter function

    person.name.toLowerCase().includes(value.toLowerCase()) this should be returned for filter to work


    part 2. Why does backspace not work?

    • now initially your list is [jim, joe]

    • on searching for jo it becomes [joe]

    • when you press backspace the array is still [joe]

    • you have removed jim from that array so it will not appear in the list when you press backspace

    Login or Signup to reply.
  2. I want to know why adding that conditional logic worked and solved the problem?

    It’s not the conditional logic that fixed it, it’s the fact that your filter function is now returning a value. You started with this:

    persons.filter(person => {
      person.name.toLowerCase().includes(value.toLowerCase())
    });
    

    The above function has curly brackets marking out a function body, but it does not have a return statement. You then switched to this:

    persons.filter((person) =>
      person.name.toLowerCase().includes(value.toLowerCase())
    )
    

    The curly brackets are now gone. This means you’re using the shorthand version of an arrow function. In the shorthand syntax, javascript automatically assumes a return .

    But I backspace to just J, it does not revert back to display both Joe and Jack in the list and still displays just Joe.

    That’s because you’ve deleted that data. Each time you type a letter, you shrink the array and set state. .filter is never going to increase the size of the array, so when you remove a letter, it will just keep the array the same size it already is.

    The fix is to not change persons; let that contain the full set of data. You’ll then calculate the filtered list from that, during rendering:

    const Search = ({ persons, setPersons }) => {
      const [filter, setFilter] = useState("");
      const filteredPersons = persons.filter((person) =>
        person.name.toLowerCase().includes(filter.toLowerCase()),
      );
    
      const handleFilterEntry = (event) => {
        const value = event.target.value;
        setFilter(value);
      };
      return (
        <div>
          Filter names with: <input value={filter} onChange={handleFilterEntry} />
        </div>
      );
    };
    

    You can improve the performance of this with useMemo so you only recalculate filteredPersons if something relevant has changed:

    const filteredPersons = useMemo(() => {
      return persons.filter((person) =>
        person.name.toLowerCase().includes(filter.toLowerCase()),
      );
    }, [persons, filter]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search