Tekla
Tekla

Reputation: 45

Having problems with searchbar logic

so I'm a beginner and I'm trying to build a simple phonebook, where you can add persons and filter them with their name.

My problem is, that when I'm trying to add a new contact, the new contact does't show until I write something in the searchbar. When I deleted the search component, I was able to add contacts normally.

Here is my code so far: (App.js)

import { useState } from 'react'
import Person from './components/Person'


const App = () => {

  const [persons, setPersons] = useState([
    { name: 'Arto Hellas', number: '040-123456', id: 1 },
    { name: 'Ada Lovelace', number: '39-44-5323523', id: 2 },
    { name: 'Dan Abramov', number: '12-43-234345', id: 3 },
    { name: 'Mary Poppendieck', number: '39-23-6423122', id: 4 }
  ])

  const [newName, setNewName] = useState('')
  const [newNumber, setNewNumber] = useState('')
  const[newSearch, setNewSearch] = useState('')
  const[personsFilter, setPersonsFilter] = useState(persons)

  const addContact = (event) => {
    event.preventDefault()
    const nameObject = {
      name: newName,
      number: newNumber,
      id: persons.length+1,
    }

  const currentPerson = persons.filter((person) => person.name === newName);
      if (currentPerson.length === 1) {
        alert(`${newName} is already added to phonebook`)
      } else {
      setPersons(persons.concat(nameObject))
      setNewName('')
      setNewNumber('')
    }
  }

  const handleNameChange = (event) => {
    setNewName(event.target.value)
  }

  const handleNumberChange = (event) => {
    setNewNumber(event.target.value)
  }
  
  const filterPersons = (event) => {
    const searchName = event.target.value.toLowerCase()
    setNewSearch(searchName)
    const newPersons = persons.filter (
      (person) => 
        person.name.toLowerCase().search(searchName) !== -1
    )
    setPersonsFilter(newPersons)
  }

  return (
    <div>
      <h2>Phonebook</h2>
      <div>
        filter:
        <input value={newSearch}
               onChange={filterPersons}
        />
      </div>
      <form onSubmit={addContact}>
        <div>
          name: <input 
                  value={newName}
                  onChange={handleNameChange}
          />
        </div>
        <div>
          number: <input
                  value={newNumber}
                  onChange={handleNumberChange}/>
          </div>
        <div>
          <button type="submit">add</button>
        </div>
      </form>
      <h2>Numbers</h2>
      <ul>
          <Person persons={personsFilter} /> 
      </ul>
    </div>
  )
}

export default App


(Persons.js)

import React from "react";

const Person = ({ persons }) => {
    return persons.map((person) => 
        <li key={person.id}>{person.name}: <span>{person.number}</span></li>
    )
}
  
  export default Person

and index.js

import React from 'react';
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))

I hope I explained my error clearly lol Thanks in advance Have great day <3

Upvotes: 0

Views: 75

Answers (1)

Bas
Bas

Reputation: 1433

The problem with your code is when you set a new person it only updates the persons state not the personsFilter state. The personsFilter state is the one being used to show data to the DOM. The reason it works when you filter is because your function filterPersons() takes a copy of the persons state.

Instead of focusing on two states why not just use a single searchTerm state then just filter that when you map?

This is how I would do it:

App.js

import { useState } from 'react'
import Person from './components/Person'

const App = () => {

  const [persons, setPersons] = useState([
    { name: 'Arto Hellas', number: '040-123456', id: 1 },
    { name: 'Ada Lovelace', number: '39-44-5323523', id: 2 },
    { name: 'Dan Abramov', number: '12-43-234345', id: 3 },
    { name: 'Mary Poppendieck', number: '39-23-6423122', id: 4 }
  ])

  const [newName, setNewName] = useState('')
  const [newNumber, setNewNumber] = useState('')
  const[searchTerm, setSearchTerm] = useState('')

  const addContact = (event) => {
    event.preventDefault()
    const nameObject = {
      name: newName,
      number: newNumber,
      id: persons.length+1,
    }

  const currentPerson = persons.filter((person) => person.name === newName);
      if (currentPerson.length === 1) {
        alert(`${newName} is already added to phonebook`)
      } else {
      setPersons(persons.concat(nameObject))
      setNewName('')
      setNewNumber('')
    }
  }

  const handleNameChange = (event) => {
    setNewName(event.target.value)
  }

  const handleNumberChange = (event) => {
    setNewNumber(event.target.value)
  }
  
  const filterPersons = (event) => {
    setSearchTerm(event.target.value.toLowerCase())
  }

  return (
    <div>
      <h2>Phonebook</h2>
      <div>
        filter:
        <input value={searchTerm}
               onChange={filterPersons}
        />
      </div>
      <form onSubmit={addContact}>
        <div>
          name: <input 
                  value={newName}
                  onChange={handleNameChange}
          />
        </div>
        <div>
          number: <input
                  value={newNumber}
                  onChange={handleNumberChange}/>
          </div>
        <div>
          <button type="submit">add</button>
        </div>
      </form>
      <h2>Numbers</h2>
      <ul>
          <Person persons={persons} searchTerm={searchTerm} /> 
      </ul>
    </div>
  )
}

export default App

Person.js

const Person = ({ persons, searchTerm }) => {
  return persons
    .filter((person) => person.name.toLowerCase().includes(searchTerm))
    .map((person) => (
    <li key={person.id}>
      {person.name}: <span>{person.number}</span>
    </li>
  ));
};

export default Person;

https://codesandbox.io/s/silly-architecture-iu8qkz?file=/src/App.js

Upvotes: 1

Related Questions