Reputation: 23
I'm quite new to Hooks and I am trying to build a small address book.
So I have two components:
I want cards to be removed when the X is clicked. I managed to toggle the deleted prop of my contact, but I can't figure out how to force re-render the ContactsList
then
import React, { useState } from 'react'
import ContactsList from './components/contacts-list/contacts-list.component'
import './App.scss'
function App() {
const [contacts] = useState([
{
key: 0,
name: 'Lennon',
firstname: 'John',
notes: 'smart guy',
deleted: false
},
{
key: 1,
name: 'Starr',
firstname: 'Ringo',
notes: 'funny guy',
deleted: false
}
])
return (
<div className='App'>
<ContactsList contacts={contacts} />
</div>
)
}
export default App
import React, { useState, useEffect } from 'react'
import ContactCard from '../contact-card/contact-card.component'
import './contacts-list.styles.scss'
function ContactsList(props) {
const [contacts, setContacts] = useState(props.contacts)
return (
<div className='contacts-list'>
<span className='title'>Contacts</span>
{contacts
.filter(contact => contact.deleted === false)
.map(contact => (
<ContactCard
name={contact.name}
firstname={contact.firstname}
notes={contact.notes}
deleted={contact.deleted}
/>
))}
<hr />
</div>
)
}
export default ContactsList
import React, { useState } from 'react'
import './contact-card.styles.scss'
function ContactCard(props) {
const [contact, setContact] = useState([
{
name: props.name,
firstname: props.firstname,
notes: props.notes,
deleted: false
}
])
function deleteContact() {
const currentContact = [...contact]
currentContact[0].deleted = true
setContact(currentContact)
}
return (
<div className='contact-card'>
<span className='contact-name'>{props.name}</span>
<span className='delete-contact' onClick={deleteContact}>
✕
</span>
<br />
<span className='contact-firstname'>{props.firstname}</span>
<hr className='separator' />
<span className='contact-notes'>{props.notes}</span>
</div>
)
}
export default ContactCard
Upvotes: 2
Views: 234
Reputation: 1964
Really a few options here, the simplest is probably just to pass in an 'onContactDeleted' prop and callback to the parents to let them know to update the state. This method isn't always the cleanest, especially with highly nested components but I would recommend it as a start is as it really is the most vanilla way that will help you understand how prop and state changes work. Note that I kept your soft delete method but you could also just remove it from the list.
Card
import React, { useState } from 'react'
import './contact-card.styles.scss'
function ContactCard(props) {
function deleteContact(key) {
props.onContactDeleted(key)
}
return (
<div className='contact-card'>
<span className='contact-name'>{props.name}</span>
<span className='delete-contact' onClick={() => deleteContact(props.contactKey)}>
✕
</span>
<br />
<span className='contact-firstname'>{props.firstname}</span>
<hr className='separator' />
<span className='contact-notes'>{props.notes}</span>
</div>
)
}
export default ContactCard
List
import React, { useState, useEffect } from 'react'
import ContactCard from '../contact-card/contact-card.component'
import './contacts-list.styles.scss'
function ContactsList(props) {
const [contacts, setContacts] = useState(props.contacts)
return (
<div className='contacts-list'>
<span className='title'>Contacts</span>
{contacts
.filter(contact => contact.deleted === false)
.map(contact => (
<ContactCard
contactKey={contact.key}
name={contact.name}
firstname={contact.firstname}
notes={contact.notes}
deleted={contact.deleted}
onContactDeleted={props.onContactDeleted}
/>
))}
<hr />
</div>
)
}
export default ContactsList
App
import ContactsList from './components/contacts-list/contacts-list.component'
import './App.scss'
function App() {
const [contacts, setContacts] = useState([
{
key: 0,
name: 'Lennon',
firstname: 'John',
notes: 'smart guy',
deleted: false
},
{
key: 1,
name: 'Starr',
firstname: 'Ringo',
notes: 'funny guy',
deleted: false
}
])
return (
<div className='App'>
<ContactsList contacts={contacts}
onContactDeleted={
(key_to_delete) => {
//note this might not be correct, use it as pseudocode
var copy = [...contacts]
var contact = copy.find(x => x.key == key_to_delete)
if(contact)
{
contact.deleted = true;
setContacts(copy)
}
}
}/>
</div>
)
}
export default App
Once you have that you could use something like redux or the useContext
hook to share the state and "cut out the middle man"
Here is an example of the useContext
hook that I quickly found online, not sure how good it is
Upvotes: 1
Reputation: 2930
Starting from your App
, I would suggest you to move your contacts object to a constant, as useState
is not needed at this level.
// constants.js
export const contacts = [
{
key: 0,
name: 'Lennon',
firstname: 'John',
notes: 'smart guy'
},
{
key: 1,
name: 'Starr',
firstname: 'Ringo',
notes: 'funny guy'
}
]
};
// App.js
import React from 'react';
import { contacts } from './constants';
import ContactsList from './components/contacts-list/contacts-list.component'
import './App.scss'
function App() {
return (
<div className='App'>
<ContactsList contacts={contacts} />
</div>
)
}
export default App
Moving on ContactList
component, as it's the component that renders each contact, I would build my state here. In that why, I would know if I need to render a contact or not beforehand.
import React, { useState } from 'react'
import ContactCard from '../contact-card/contact-card.component'
import './contacts-list.styles.scss'
function ContactsList(props) {
const [contacts, setContacts] = useState(props.contacts);
const handleDeletion = id => {
setContacts(contacts.filter(contact => contact.id !== id));
}
return (
<div className='contacts-list'>
<span className='title'>Contacts</span>
{contacts.length ?
contacts.map(contact =>
<ContactCard
id={contact.id}
name={contact.name}
firstname={contact.firstname}
notes={contact.notes}
handleDeletion={handleDeletion}
/>
) : null}
<hr />
</div>
)
}
export default ContactsList
Notice that I am passing the function that handles the removal to my ContactCard, while I am still deciding here if I should show my contact.
import React from 'react'
import './contact-card.styles.scss'
function ContactCard(props) {
return (
<div className='contact-card'>
<span className='contact-name'>{props.name}</span>
<span className='delete-contact' onClick={() => props.handleDeletion(props.id)}>
✕
</span>
<br />
<span className='contact-firstname'>{props.firstname}</span>
<hr className='separator' />
<span className='contact-notes'>{props.notes}</span>
</div>
)
}
export default ContactCard
I haven't tried the code, but I think you should move at this path.
Upvotes: 0