React Router v6导航后BookmarkList组件数据未更新问题求助
问题描述
使用react-router-dom v6开发项目,BookmarkList组件负责展示书签列表,支持添加、删除书签操作。当前遇到的问题:在AddBookmark组件中添加书签后,调用navigate("/bookmark")导航至BookmarkList组件,组件虽会重新渲染,但自定义Hook未从服务器获取最新书签列表(刷新页面可正常显示最新数据)。
相关代码
BookmarkList组件
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> ); }
AddBookmark组件
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, 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(data.city); setCountryNameState(data.countryName); } }, [data]); if (isLoading) return <div>Loading ...</div>; function handleSubmit(e) { e.preventDefault(); const newBookmark = { cityName: cityNameState || data.locality, countryName: countryNameState, countryCode: data.countryCode, latitude: `${data.latitude}`, longitude: `${data.longitude}`, host_location: data.city + " " + data.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) => setCityNameState(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) => setCountryNameState(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> ); }
useFetch自定义Hook
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 }; }
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>
已尝试在BookmarkList组件中添加useEffect、修改路由路径为"/bookmark/",均未解决问题。
问题分析与解决
核心问题在于书签添加完成后,没有触发服务器端书签列表的重新获取,且你未提供BookmarksProvider的实现,这是关键逻辑环节。结合现有代码推断,给出以下解决步骤:
1. 完善addBookmark的异步提交逻辑
确保addBookmark先将新书签提交到服务器,成功后再触发列表刷新:
// BookmarksProvider中的addBookmark示例实现 const addBookmark = async (newBookmark) => { try { // 提交新书签到服务器 await axios.post('/api/bookmarks', newBookmark); // 提交成功后,重新拉取最新书签列表 fetchBookmarks(); // fetchBookmarks是你定义的获取服务器书签列表的方法 } catch (err) { toast.error('添加书签失败'); } };
2. 给useFetch添加手动刷新能力
修改useFetch,使其支持手动触发请求,方便在添加书签后主动刷新数据:
export default function useFetch(url, query = "") { const [data, setData] = useState(); const [isLoading, setIsLoading] = useState(true); const fetchData = async () => { setIsLoading(true); try { const response = await axios.get(`${url}?${query}`); setData(response.data); } catch (err) { toast.error(err.message); } finally { setIsLoading(false); } }; useEffect(() => { fetchData(); }, [url, query]); return { isLoading, data, fetchData }; // 返回手动刷新方法 }
3. 调整导航时机
等待书签提交并刷新列表完成后,再执行导航操作:
// AddBookmark组件的handleSubmit修改为异步函数 async function handleSubmit(e) { e.preventDefault(); const newBookmark = { cityName: cityNameState || data.locality, countryName: countryNameState, countryCode: data.countryCode, latitude: `${data.latitude}`, longitude: `${data.longitude}`, host_location: data.city + " " + data.countryName, id: uuidv4(), }; await addBookmark(newBookmark); // 等待添加操作完成 navigate("/bookmark"); }
可选:添加乐观更新提升体验
如果想优化用户体验,可以先在本地状态添加新书签,再同步到服务器,失败时回滚:
// BookmarksProvider中的addBookmark优化 const addBookmark = async (newBookmark) => { // 先本地添加(乐观更新) setBookmarks(prev => [...prev, newBookmark]); try { await axios.post('/api/bookmarks', newBookmark); } catch (err) { // 提交失败,回滚本地状态 setBookmarks(prev => prev.filter(item => item.id !== newBookmark.id)); toast.error('添加书签失败'); } };
内容的提问来源于stack exchange,提问作者alireza naghavi
相关产品推荐
相关产品推荐

