Reputation: 123
Im making a CRUD blog like application and when I POST or EDIT a blog I redirect the user using history.push() however when the user is redirected the information is still the old one if I refresh the page the content is updated. I have looked everywhere for the past day but I cant seem to find the answer to my problem. Here is the Component where I update information for example
import { useState, useEffect, useRef } from "react";
import { useParams, useHistory } from "react-router";
const EditBanner = (props) => {
const [imgValue, setImgValue] = useState('');
const [descriptionValue, setDescriptionValue] = useState('');
const params = useParams();
const history = useHistory();
const imgRef = useRef();
const descriptionRef = useRef();
useEffect(() => {
const fetchBlog = async () => {
const response = await fetch(`URL/banners/${params.id}.json`);
const data = await response.json();
setImgValue(data.img);
setDescriptionValue(data.description);
}
fetchBlog();
},[params])
const onSubmitHandler = (e) => {
e.preventDefault()
fetch(`URL/banners/${params.id}.json`,{
method: "PUT",
body: JSON.stringify({
description: descriptionRef.current.value,
img: imgRef.current.value
}),
headers: {
'Content-Type': 'application/json'
}
})
history.push(`/banners/${params.id}`);
}
const imgUrlChangeHandler = () => {
setImgValue(imgRef.current.value)
}
const descriptionChangeHandler = () => {
setDescriptionValue(descriptionRef.current.value)
}
return (
<form onSubmit={onSubmitHandler}>
<input type="text" placeholder="img url" ref={imgRef} value={imgValue} onChange={imgUrlChangeHandler}></input>
<textarea ref={descriptionRef} value={descriptionValue} onChange={descriptionChangeHandler}></textarea>
<button>Submit</button>
</form>
)
}
export default EditBanner;
And here is the Detail page which I redirect to with the useHistory() hook. Keep in mind if I add the bannerDetail as a dependancy in the useEffect hook it will work but then I will create a infinite loop,
import { useEffect, useState } from 'react';
import { useParams } from 'react-router';
import { useHistory } from 'react-router';
import styles from './BannerDetail.module.css';
const BannerDetail = (props) => {
const history = useHistory();
const params = useParams();
const [bannerDetail, setBannerDetail] = useState({});
useEffect(() => {
const fetchBanner = async () => {
const response = await fetch(`URL/banners/${params.id}.json`);
const data = await response.json();
console.log(data)
setBannerDetail(data);
}
console.log('useEffect run in bannerDetail')
fetchBanner();
},[params])
const onEditHandler = (e) => {
e.preventDefault();
history.push(`/banners/${params.id}/edit`)
}
const onDeleteHandler = (e) => {
e.preventDefault();
fetch(`URL/banners/${params.id}.json`,{
method: "DELETE",
headers: {
'Content-Type': 'application/json'
}
})
history.replace('/banners')
}
return (
<section className={styles.detail}>
<div className={styles['image-container']}>
<img
src={bannerDetail.img}
alt={bannerDetail.description}
/>
</div>
<div className={styles['description-container']}>
<p>{bannerDetail.description}</p>
</div>
<div className={styles.actions}>
<button onClick={onEditHandler}>Edit</button>
<button onClick={onDeleteHandler}>Delete</button>
</div>
</section>
)
}
export default BannerDetail;
Also here are all my ROUTES,
<Switch>
<Route path="/" exact><Redirect to="/banners"/></Route>
<Route path="/banners" exact> <Banners/> </Route>
<Route path="/new-banner" exact>
<AddNewUser/>
</Route>
<Route path="/banners/:id/edit" exact>
<EditBanner/>
</Route>
<Route path="/banners/:id" >
<BannerDetail/>
</Route>
</Switch>
Upvotes: 4
Views: 2153
Reputation: 202686
I think the issue you have is that you are dispatching a PUT request and then immediately navigating to the new page where a GET request is made when the component mounts. There is no guarantee in the order in which network requests resolve.
You may want to wait for the PUT request to resolve first before navigating to the next page.
const onSubmitHandler = (e) => {
e.preventDefault();
fetch(`URL/banners/${params.id}.json`,{
method: "PUT",
body: JSON.stringify({
description: descriptionRef.current.value,
img: imgRef.current.value
}),
headers: {
'Content-Type': 'application/json'
}
}).finally(() => {
// regardless of fetch resolve/reject, navigate to new page
history.push(`/banners/${params.id}`);
});
}
Perhaps here though you may only want to navigate to the next page if the fetch
resolves, or only with a 200OK response or what have you, use a .then
block for this. Maybe you want to handle rejected responses to display an error message to the user, use the .catch
block for this.
const onSubmitHandler = (e) => {
e.preventDefault();
fetch(`URL/banners/${params.id}.json`,{
method: "PUT",
body: JSON.stringify({
description: descriptionRef.current.value,
img: imgRef.current.value
}),
headers: {
'Content-Type': 'application/json'
}
})
.then((response) => {
if (response.ok) throw new Error('response not ok');
history.push(`/banners/${params.id}`);
})
.catch(error => {
// handle errors
});
}
If you prefer async/await
over Promise chains:
const onSubmitHandler = async (e) => {
e.preventDefault();
try {
const response = await fetch(`URL/banners/${params.id}.json`,{
method: "PUT",
body: JSON.stringify({
description: descriptionRef.current.value,
img: imgRef.current.value
}),
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) throw new Error('response not ok');
history.push(`/banners/${params.id}`);
} catch(error) {
// handle errors
}
}
Upvotes: 2