first of all my project link on github: https://github.com/ARN1380/BookingHotel
I'm using react-router dom v6, I have a BookmarkList in my project and I need to add or delete bookmarks from it, the problem is when I add a bookmark from another component I need to navigate to the BookmarkList component. This navigation was supposed to rerender the BookmarkList but it does not so my custom Hook doesn't run and new list doesn't get fetch from server. (if i refresh the page it does fetch and everything is ok) edit: it does rerender here is my BookmarkList component:
import ReactCountryFlag from "react-country-flag";
import { useBookmarks } from "../context/BookmarksProvider";
import { Link } from "react-router-dom";
import { closePopup } from "../map/Map";
import { HiTrash } from "react-icons/hi";
export default function BookmarkList() {
const {bookmarks, deleteBookmark} = useBookmarks();
function handleBookmarkDelete(e, id) {
e.preventDefault();
deleteBookmark(id);
}
return (
<div>
<h2>Bookmark List</h2>
<div className="bookmarkList">
{bookmarks.map((bookmark) => {
return (
<Link
key={bookmark.id}
to={`${bookmark.id}?lat=${bookmark.latitude}&lng=${bookmark.longitude}`}
onClick={closePopup}
>
<div className="bookmarkItem">
<div>
<ReactCountryFlag svg countryCode={bookmark.countryCode} />
<strong>{bookmark.cityName}</strong> {" "}
<span>{bookmark.country}</span>
</div>
<button
type="button"
onClick={(e) => {
handleBookmarkDelete(e, bookmark.id);
}}
>
<HiTrash className="trash" />
</button>
</div>
</Link>
);
})}
</div>
</div>
);
}
and this is AddBookmark component:
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAnglesLeft } from "@fortawesome/free-solid-svg-icons";
import { useNavigate, useSearchParams } from "react-router-dom";
import useFetch from "../../hooks/useFetch";
import { useBookmarks } from "../context/BookmarksProvider";
import { useEffect, useId, useState } from "react";
import {v4 as uuidv4} from 'uuid';
export default function AddBookmarks() {
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const [cityNameState, setCityNameState] = useState("");
const [countryNameState, setCountryNameState] = useState("");
const {addBookmark} = useBookmarks();
const lat = searchParams.get("lat");
const lng = searchParams.get("lng");
const {isLoading, data} = useFetch(
"https://api.bigdatacloud.net/data/reverse-geocode-client",
`latitude=${lat}&longitude=${lng}`
);
useEffect(() => {
if (data) {
setCityNameState(locationData.city);
setCountryNameState(locationData.countryName);
}
}, [data]);
if (isLoading) return <div>Loading ...</div>;
const locationData = data;
function handleSubmit(e) {
e.preventDefault();
const newBookmark = {
cityName: cityNameState || locationData.locality,
countryName: countryNameState,
countryCode: locationData.countryCode,
latitude: `${locationData.latitude}`,
longitude: `${locationData.longitude}`,
host_location: locationData.city + " " + locationData.countryName,
id: uuidv4(),
};
addBookmark(newBookmark);
navigate("/bookmark");
}
return (
<div className="capitalize">
<h2 className="font-bold">add bookmark</h2>
<form className="mt-2 [&>div>label]:text-sm" onSubmit={handleSubmit}>
<div className="flex flex-col">
<label className="">city:</label>
<input
className="border-b border-0 border-solid mt-2"
type="text"
required
value={cityNameState}
onChange={(e) => setCityName(e.target.value)}
/>
</div>
<div className="flex flex-col mt-4">
<label>country:</label>
<input
className="border-b border-0 border-solid mt-2"
type="text"
required
value={countryNameState}
onChange={(e) => setCountryName(e.target.value)}
/>
</div>
<div className="mt-5 flex justify-between">
<button
onClick={() => {
navigate(-1);
}}
type="button"
className="border border-slate-400 border-solid rounded-md flex space-x-1 items-center p-0.5 px-1"
>
<FontAwesomeIcon icon={faAnglesLeft} size="xs" />
<p>Back</p>
</button>
<button className="bg-purple-600 text-white px-3 py-0.5 rounded-md">
save
</button>
</div>
</form>
</div>
);
}
and this is my useFetch custom hook in case:
import axios, { AxiosError } from "axios";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
export default function useFetch(url, query = "") {
const [data, setData] = useState();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setIsLoading(true);
axios
.get(`${url}?${query}`)
.then((response) => {
setData(response.data);
})
.catch((err) => {
toast.error(err.message);
})
.finally(() => setIsLoading(false));
}, [url, query]);
return {isLoading, data};
}
here is my routing in App.jsx:
<Routes>
<Route path="/" element={<HotelsList />} />
<Route path="/search" element={<SearchLayout />}>
<Route index element={<Hotels />} />
<Route path="Hotels/:id" element={<SingleHotel />} />
</Route>
<Route path="/bookmark" element={<BookmarkLayout />}>
<Route index element={<BookmarkList />} />
<Route path=":id" element={<SingleBookmark />} />
<Route path="add" element={<AddBookmarks /> } />
</Route>
<Route path="/404" element={<ErrorPage />} />
<Route path="*" element={<Navigate to="/404" />} />
</Routes>
what is my problem? i can't see why BookmarkList doesn't rerender when navigate happens ...
i tried using an useEffect in BookmarkList component but it didn't work
adding path="/bookmark/" to
<Route path="/bookmark/" index element={} />
In your bookmarksProvider you have this code:
let [isLoading, data] = useFetch(BOOKMARK_URL);
if (isLoading) return <div > Loading... < /div>;
function addBookmark(newBookmark) {
axios
.post(BOOKMARK_URL, newBookmark)
.then((response) => {})
.catch((error) => {
toast.error("couldn't save your bookmark!");
console.log(error.response.data);
});
}
In your addBookmark
function, you only POST to the url. I'm assuming you then want useFetch
to automatically refetch bookmarks. That won't happen because of the useEffect you use in useFetch
. The useFetch will only rerun when url
or query
changes. Since those are hardcoded, useEffect
will be run once and never again (unless you refresh your window of course).
You will need to create a way to either add the new bookmark to your data array, or add a new parameter to useFetch
, that you can update with a new unique value so that useEffect within useFetch will run again.
Another option is to add a function to your context (getBookmarks
for instance) that you can call with useEffect
inside of your list page so that everytime you visit the list page, the bookmarks are fetched.