I have a pretty simple component that displays a list of contacts via an API call.
allContacts.jsx
import React, { useEffect, useState } from 'react';
import PhoneLayout from './layouts/phoneLayout';
import AddContact from './addContact';
import ShowContact from './showContact';
export default function AllContacts() {
let [contacts, setContacts] = useState([]);
let [showContact, setShowContact] = useState(false);
useEffect(() => {
fetch(`http://localhost:8000/all-contacts`)
.then((response) => response.json())
.then((data) => setContacts(data))
.catch((err) => console.log(err));
}, []);
return (
<PhoneLayout>
<div className="relative">
<AddContact />
<div className="flex flex-col">
{contacts.map((contact) => (
<button onClick={() => setShowContact(true)}>
{showContact ? (
<ShowContact contactId={contact.id} open={true} />
) : (
<div className="border-b border-gray-200 p-4 py-2 text-left capitalize text-gray-700 hover:bg-gray-200">
<span className="font-bold">{contact.first_name}</span>{' '}
{contact.last_name}
</div>
)}
</button>
))}
</div>
</div>
</PhoneLayout>
);
}
I am displaying another component and passing some props
with it.
<ShowContact contactId={contact.id} open={true} />
The trouble is that the API call in ShowContact
seems to be calling it for every entry I have in my API db. On click I get the following in the console:
showContact.jsx:17 {contactId: 4, open: true}
showContact.jsx:17 {contactId: 5, open: true}
showContact.jsx:17 {contactId: 6, open: true}
showContact.jsx:17 {contactId: 7, open: true}
showContact.jsx:17 {contactId: 8, open: true}
showContact.jsx:17 {contactId: 9, open: true}
showContact.jsx:17 {contactId: 10, open: true}
showContact.jsx:17 {contactId: 11, open: true}
showContact.jsx:17 {contactId: 12, open: true}
showContact.jsx:17 {contactId: 13, open: true}
showContact.jsx:17 {contactId: 14, open: true}
showContact.jsx:17 {contactId: 15, open: true}
showContact.jsx:17 {contactId: 16, open: true}
showContact.jsx:17 {contactId: 17, open: true}
This is the ShowContact
components
// showContact.jsx
export default function ShowContact(props) {
let [isOpen, setIsOpen] = useState(false);
let [contact, setContact] = useState({});
// delete contact from api then reloads the page
function deleteContact() {
fetch(`http://localhost:8000/delete-contact/${props.contactId}`, {
method: 'DELETE',
}).catch((err) => console.log(err));
setIsOpen(false);
window.location.reload(true);
}
console.log(props);
// get contact from api by contact id
useEffect(() => {
async function fetchContact() {
await fetch(`http://localhost:8000/get-contact/${props.contactId}`)
.then((response) => response.json())
.then((data) => {
setContact(data);
console.log(data);
})
.catch((err) => console.log(err));
}
if (!props.open) {
fetchContact();
}
}, []);
return ( ...
I have inverted the conditional within the uesEffect
block so I can show you all whats happening. I have used this useEffect
in this way in the pass, however the component was slightly different. Like so:
export default function MyModal(props) {
let [isOpen, setIsOpen] = useState(false);
let completeButtonRef = useRef(null);
function closeModal() {
setIsOpen(false);
}
function openModal() {
setIsOpen(true);
}
// get contact from api by contact id
useEffect(() => {
async function fetchContact() {
await fetch(`http://localhost:8000/get-contact/${props.contactId}`)
.then((response) => response.json())
.then((data) => {
setContact(data);
console.log(data);
})
.catch((err) => console.log(err));
}
if (isOpen) {
fetchContact();
}
}, [isOpen]);
return (
<>
<button
type="button"
onClick={openModal}
ref={completeButtonRef}
className="rounded-md bg-black bg-opacity-20 px-4 py-2 text-sm font-medium text-white hover:bg-opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
>
Edit
</button>
As you can see here the above component had the button which would set isOpen
where as the component I am trying to use does not have this, therefore it seems to be calling the API for every entry. This is why I thought about passing a boolean with a props, but this just seems to fire the useEffect
again again.
I have looked through:
- UseEffect being called multiple times
- And many others one fixed my original issue with the
above example
but I am re designing it and I don't want an edit button