I’m currently learning react with its documentation, I give myself a challange and wanted to create a small chat app. I have an understanding some fundemantals and I have a problem with states.
In my code there is a contacts object which holds the information for my contacts. I used a state to contain it and created three buttons with each persons name in it. For the buttons I used a component with two props handleButtonClick and contacts.
const contacts = [
{id: 0, name: 'Taylor', email: 'taylor@mail.com'},
{id: 1, name: 'Alice', email: 'alice@mail.com'},
{id: 2, name: 'Bob', email: 'bob@mail.com'},
];
const [contactList, setContactList] = useState(contacts);
<ContactList handleButtonClick={handleButtonClick} contacts={contactList} />
Inside of the component :
export default function ContactList({ handleButtonClick, contacts }) {
return (
<div className="contact-buttons">
{contacts.map((contact) => {
return (
<button
key={contact.id}
className="personName"
onClick={handleButtonClick}
value={contact.id}
>
{contact.name}
</button>
);
})}
</div>
);
}
The thing I want to do is when handleButtonClick executes, it needs to update a state with the id of the person and with that id I want to find the correct person with contactList.find.
const [selectedId, setSelectedId] = useState(0);
const contact = contactList.find((c) => c.id === selectedId);
function handleButtonClick(e){
e.preventDefault()
setSelectedId(e.target.value);
}
And the problem is, for the first person contact starts correctly but it never gets updated. If i am not mistaken when I set the selectedId it should trigger a re-render and set contact. What am I missing ? Thank you !!
>Solution :
You’re changing the type of what’s stored in your selectedId state when you update it. Initially, it starts off as a number type, which aligns with the type of your id properties in your contacts objects. That’s why when you call .find() initially it can find the contact correctly. However, when you update your state to e.target.value, you’re setting a string version of your id to the state (that’s because the .value property returns a string). As you are using strict equality comparison (===) in your .find() method, you’ll find that your updated selectedId won’t match any of the objects as the types are different (you’re now comparing numeric ids from your objects with your string state id).
You have a couple of options to fix this. The first is to use loose equality comparison (==), rather than strict comparison ===, which doesn’t care about if the types are different:
const contact = contactList.find((c) => c.id == selectedId);
The == can behave a bit unexpectedly in some scenarios, so usually, it’s best to stick with ===. Instead, I’d suggest changing your state updating code to set your state to be a number rather than a string. Here I’m doing that using the unary plus operator +, but you can use something like Number() to achieve the same result:
setSelectedId(+e.target.value); // or `Number(e.target.value)`
Alternatively, you can ditch the value prop on the button, and instead pass through the selected contact into the function call like so, this way you avoid having to iterate your contacts on each render or when your selected id changes:
<button
key={contact.id}
className="personName"
onClick={() => handleButtonClick(contact)}
>
Then your handler and state would set and hold a contact object (if possible):
const [selectedContact, setSelectedContact] = useState(() => contacts[0]);
function handleButtonClick(contact){
e.preventDefault();
setSelectedContact(contact);
}