I'm working on a phonebook app. I'm taking user input from a form and then adding it to my state and then applying the map method on that state to get the user details. Here is my code:
import React, { useState, useEffect } from "react";
const phoneBook = [];
const App = () => {
const [contacts, setContacts] = useState(phoneBook);
const [showForm, setShowForm] = useState(false);
const [userName, setUserName] = useState("");
const [number, setNumber] = useState("");
const handleDelete = (name) => {
let newPhoneBook = contacts.filter((contact) => contact.name !== name);
setContacts(newPhoneBook);
};
const handleAddContact = (userName, number) => {
let obj = { name: userName, number: number };
console.log("obj:", obj);
phoneBook.push(obj);
console.log("contacts:", contacts);
console.log("phoneBook:", phoneBook);
};
return (
<div id="main">
<div className="container-fluid">
<div className="row">
<div className="col-md-4"></div>
<div className="col-md-4">
<div className="App">
<h2 className="header">PhoneBook App</h2>
<span
style={{
cursor: "pointer",
color: "blue",
textDecoration: "underline",
}}
onClick={() => setShowForm(!showForm)}
>
{showForm ? "Hide Contact Form" : "Create New Contact"}
</span>
{showForm && (
<div className="container">
<form className="form">
<div class="form-group">
<input
type="text"
className="form-control"
placeHolder="Name"
value={userName}
onChange={(e) => {
e.target.value;
setUserName(e.target.value);
console.log("userName:", userName);
console.log("e.target.value:", e.target.value);
}}
/>
</div>
<div class="form-group">
<input
type="text"
className="form-control"
placeHolder="Number"
value={number}
onChange={(e) => {
e.target.value;
setNumber(e.target.value);
console.log("number:", number);
console.log("e.target.value:", e.target.value);
}}
/>
</div>
<button
type="button"
className="btn btn-primary icon-holder"
onClick={() => {
handleAddContact(userName, number);
}}
>
Add
</button>
</form>
</div>
)}
<p>
There are {contacts.length} contacts saved in this book yet.
</p>
{contacts.map((contact, index) => (
<div key={index} className="contacts">
<div class="contact-details">
<h5>{contact.name}</h5>
<p>{contact.number}</p>
</div>
<button
class="icon-holder"
onClick={() => {
handleDelete(contact.name);
}}
>
Delete
</button>
<hr />
</div>
))}
</div>
</div>
<div className="col-md-4"></div>
</div>
</div>
</div>
);
};
export default App;
The issue I'm facing is that the contacts are showing updated value in the console but not showing in the browser through map method. I have a clue that there is an issue of useState, like how it affects the rendering. One thing I've noted that if I made any change to my code, then the map method shows the saved users at that time.
Here is the codesandbox example for better demonstration: https://codesandbox.io/p/sandbox/todos-app-listed-schkqm?file=%2Fsrc%2FApp.js%3A8%2C48
You are mutating the state when adding a contact: Mutating the
phoneBookarray alters thecontactsstate, but doesn't set it, which means that React is not aware of the change and doesn't re-render the new values.Ditch the
phoneBookarray, and set thecontactsstate directly. Create a new array using the previous value of contacts using spread plus the new contact.I would also use the updater function in
handleDelete: