I have a this component in which I fetch data from an API and display it on screen:
const RestaurantsList = () => {
const [restaurants, setRestaurants] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch("http://localhost:3001/api/v1/restaurants");
const data = await response.json();
//console.log(data);
setRestaurants(data);
};
fetchData();
}, []);
...
I have another component AddRestaurant in which I make a Post request to the API to add a new restaurant:
const AddRestaurant = () => {
const [name, setName] = useState("");
const [location, setLocation] = useState("");
const [priceRange, setPriceRange] = useState("");
function nameUpdate(e) {
setName(e.target.value);
}
function locationUpdate(e) {
setLocation(e.target.value);
}
const handleSubmit = async function (e) {
//e.preventDefault();
try {
await fetch("http://localhost:3001/api/v1/restaurants", {
method: "POST",
made: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
location,
price_range: priceRange,
}),
});
} catch (err) {
console.log(err);
}
setName("");
setLocation("");
setPriceRange("");
};
...
The problem is that when I add a new Restaurant I want to add tho the list without refreshing the page. If I add a [restaurants] dependency on the useEffect, it keeps making GET request in loop.
Both components are child og this component:
const Home = () => {
return (
<div>
<Header />
<AddRestaurant />
<RestaurantsList />
</div>
);
};
export default Home;
What is the best solution?
CodePudding user response:
One solution would be to add a top-level state that starts off as true
and fetches initial data. We set this to false
when data fetching is done.
const Home = () => {
const [fetchRestaurants, setFetchRestaurants] = useState(true)
return (
<div>
<Header />
<AddRestaurant setFetchRestaurants={setFetchRestaurants} />
<RestaurantsList fetchRestaurants={fetchRestaurants} setFetchRestaurants={setFetchRestaurants} />
</div>
)
}
export default Home
const RestaurantsList = ({ fetchRestaurants, setFetchRestaurants }) => {
const [restaurants, setRestaurants] = useState([])
useEffect(() => {
if (fetchRestaurants) {
const fetchData = async () => {
const response = await fetch('http://localhost:3001/api/v1/restaurants')
const data = await response.json()
// console.log(data);
setRestaurants(data)
}
fetchData()
setFetchRestaurants(false)
}
}, [fetchRestaurants, setFetchRestaurants])
// ...
}
Then in AddRestaurant
:
const handleSubmit = async function (e) {
// ...
setFetchRestaurants(true)
setName('')
setLocation('')
setPriceRange('')
}
When fetchRestaurants
changes, RestaurantsList
will re-rerender and fetch new data.
A library that does a lot of this for you is RTK Query, check out the docs here:
- https://redux-toolkit.js.org/rtk-query/overview
- https://redux-toolkit.js.org/rtk-query/usage/automated-refetching
CodePudding user response:
We should use some kind of flag that will inform components about new updates.
I would recommend doing something like this.
export default function Home () {
const [
timeOfLastRestaurantCreation,
setTimeOfLastRestaurantCreation
] = useState<string>()
return (
<div>
<Header />
<AddRestaurant
onAddRestaurant={() => {
setTimeOfLastRestaurantCreation(new Date().toISOString())
}}
/>
<RestaurantsList
timeOfLastRestaurantCreation={timeOfLastRestaurantCreation}
/>
</div>
)
}
PS. Why ISOString instead of Date? Easier to debug.