Reputation: 45
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
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