Issue
I've created a notes app. On the front page it has categories and notes. You can click on the categories and get all of the notes under that category. I just added a button that lets you delete the category and all of its notes and then navigate back to the front page. It looks like this:
Button:
<IonRow>
<IonButton onClick={deletecat} >
Delete Category
</IonButton>
</IonRow>
Here is the deletecat
function:
const deletecat = () => {
const trashcategory = ({category}) => {
try {
fetch(`https://fakeurl.com/delcat/${category}/`, {
method: "DELETE",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
})
} catch (error) {
console.log("error time!", error);
return false;
}
};
trashcategory({category})
router.push('/notes')
}
When I click on my button I get this error:
NotFoundError: Node.removeChild: The node to be removed is not a child of this node
This issue was actually addressed on SO before (React Error: NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node), but their specific solution was for jquery. But I think the concept is the same:
This issue occurs when you:
1. Render something using React 2. Then, you manipulate DOM rendered by React with external script 3. Now on the next render cycle(re-render), React doesn't find the DOM node it rendered previously as its already modified/removed by external script
How do I resolve this? Is there any way I can re-render the DOM in a way where it doesn't try and look for what was rendered previously? How else might I get around this?
edit: This is the front page:
useEffect(() => {
getcategories({username})
getnotes({username})
console.log("USEEFFECTTIME")
},[]);
const getcategories = ({ username }) => {
try {
fetch(`https://fakeurl.com/getcategories`, {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({username}),
})
.then(res => res.json())
.then(data => {
setCategorydata(data);
setLoading(false);
})
} catch (error) {
console.log("error time!", error);
return false;
}
};
console.log('before get categories')
const getnotes = async ({ username }) => {
try {
//await fetch(`/getnotes`, {
await fetch(`https://fakeurl.com/getnotes`, {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({username}),
})
.then(res => res.json())
.then(data2 => {
setNotedata(data2);
setLoading2(false)
})
} catch (error) {
console.log("error time!", error);
return false;
}
};
const categories = categorydata.categories
const notes = notedata.notes
<IonSlides id="slider" options={{ slidesPerView: "auto", zoom: true, grabCursor: true }} className={ `${ styles.categorySlider } ion-padding-bottom` }>
{ categories.map((category, index) => {
const noteCount = notes.filter(n => n.note_category === category.id).length;
return (
<IonSlide key={ `categorySlide_${ index }`}>
<IonCol className="ion-text-left">
<IonCard routerLink={`/categorypage/${category.id}`}>
<IonCardHeader className="ion-no-padding" >
<div className={ styles.slideCount }>
<h6>{ noteCount } { noteCount === 1 ? "note" : "notes" } </h6>
</div>
<div className={ styles.slideHeader }>
<h4 style={{color:"black"}}>{ category.category }</h4>
</div>
</IonCardHeader>
<IonCardContent>
<div className={ styles.categoryColor } style={{ borderBottom: `2px solid ${ category.color }` }}></div>
</IonCardContent>
</IonCard>
</IonCol>
</IonSlide>
);
})}
</IonSlides>
<IonGrid className={ styles.bottomContainer }>
<IonRow>
<IonCol size="12" className="ion-padding-start">
<IonCardSubtitle className={ styles.heading }>
Recent Notes
</IonCardSubtitle>
</IonCol>
</IonRow>
<div className={ styles.recentNotes }>
{ notes.slice(0).reverse().map((note, index) => {
return (
<IonRow key={ `note_${ index }` } className="animate__animated animate__faster" id={ `noteRow_${ note.id }` }>
<IonCol size="12">
<Link to={`/Update/${note.id}`}>
<h2>{note.note_name}</h2>
</Link>
</IonCol>
</IonRow>
);
})}
</div>
</IonGrid>
Solution
Per your comments above, to make the changes reflect on both the front page and the component page, you call the data from the API at the parent component level and then pass the data and setData down as props to the component page.
If the front page and the component page are siblings under a router, then you can pull the data in the router component and share as props or implement the useContext hook to share the data.
Updates like changing note contents and deleting categories should happen on the front-end and then be updated to the back-end via fetch requests asynchronously.
The problem is coming from the asynchronous delay between updates from the API competing with synchronous unmounts with calls to the router.push
method. By relying on local state data, you should get rid of your problem.
Answered By - Jacob K
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.