React返回同路由时仅页面刷新才向Node.js发请求的实现方案
问题描述
- 首次访问根路由
/时,前端向Node.js服务端发送请求加载Feed流数据至React应用,该部分逻辑运行正常 - 现存异常:用户跳转至其他路由后再返回首页
/路由时,页面未触发主动刷新就会重复向服务端发请求,重新加载Feed流数据 - 预期逻辑:仅当用户主动刷新页面时才发起服务端数据请求;通过路由跳转返回
/路由时,直接从web storage读取已存储的Feed数据,不重复请求服务端 - 适配要求:用户通过路由跳转返回首页后,无限滚动加载帖子的逻辑需要基于此前已经加载完成的所有帖子数据继续向后加载,无需从头拉取
当前实现代码
帖子加载核心逻辑
//load posts for users const [increment, setIncrement] = useState(2) let initialPage = 0; useEffect(() => { async function loadPosts() { try { const loadPostResponse = await fetch(`${process.env.REACT_APP_API_BACKENDURL}/blob/load/all/post/${initialPage}/${increment}`, { method: "GET", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("uuid") } }) const loadPostData = await loadPostResponse.json() console.log({ loadPostData }, { increment }, { initialPage }) if (loadPostResponse.status === 200) { const Arrange = loadPostData.data.sort((a, b) => { return b.time - a.time }) if (loadingPostIsMount.current) { setAllPosts(Arrange) //set all posts into session storage if (initialPage === 0) { sessionStorage.setItem("_users_posts", JSON.stringify(Arrange)) } else if (initialPage > 0) { sessionStorage.removeItem("_users_posts") sessionStorage.setItem("_users_posts", JSON.stringify(allPosts)) } } initialPage = initialPage + 2 } } catch (err) { } } loadPosts() return () => { loadingPostIsMount.current = false } }, [increment])
注:上述代码中increment是无限滚动触发的依赖项
无限滚动实现
function loadMorePost() { setIncrement(increment + 2) } <InfiniteScroll dataLength={length} next={loadMorePost} hasMore={true} loader={<Spinner name="three-bounce" />} className="md:ml-[8rem] ml-[0rem]" endMessage={ <p>loeading end</p> } />
实现思路
- 修正状态定义缺陷:原代码中
initialPage是组件内普通局部变量,组件每次重渲染、路由返回重新挂载时都会被重置为0,且分页进度、已加载帖子列表没有做持久化同步,是导致返回路由后重复从头加载的核心原因。需要把已加载帖子列表、当前分页进度、分页步长都同步存入sessionStorage,同时作为组件状态维护。sessionStorage的生命周期天然匹配需求:标签页关闭即清除、路由跳转不丢失、主动刷新页面不丢失,无需使用localStorage。 - 增加首屏缓存判断逻辑:组件挂载进入
/路由时,优先检查sessionStorage中是否存在已缓存的帖子数据:- 存在缓存:直接读取缓存值初始化帖子列表、当前页码、分页步长状态,不发起首屏请求
- 无缓存(用户主动刷新、首次进入站点场景):才发起第一页数据的接口请求
- 修正无限滚动加载逻辑:
- 废弃原代码中的局部变量
initialPage,改用state存储当前已加载到的页码,每次加载更多成功后更新页码状态,同步写入sessionStorage - 加载更多接口返回新数据后,需要和已有帖子列表做拼接,而非直接覆盖全量列表,避免之前加载的内容丢失
- 每次帖子列表更新后,同步把最新的全量列表、当前页码、分页步长写入
sessionStorage
- 废弃原代码中的局部变量
- 拆分useEffect职责:将首屏初始化加载和加载更多的逻辑拆分到不同的useEffect中,避免逻辑互相干扰,首屏加载的useEffect仅在组件挂载时执行一次,加载更多的useEffect仅在
increment变化时触发。
参考修正代码
// 状态初始化时优先读取sessionStorage缓存 const getCachedPosts = () => { const cached = sessionStorage.getItem("_users_posts") return cached ? JSON.parse(cached) : [] } const getCachedPage = () => { const cached = sessionStorage.getItem("_users_posts_page") return cached ? Number(cached) : 0 } const getCachedIncrement = () => { const cached = sessionStorage.getItem("_users_posts_increment") return cached ? Number(cached) : 2 } const [allPosts, setAllPosts] = useState(getCachedPosts) const [currentPage, setCurrentPage] = useState(getCachedPage) const [increment, setIncrement] = useState(getCachedIncrement) const loadingPostIsMount = useRef(true) // 首屏初始化加载:仅无缓存时发起请求 useEffect(() => { loadingPostIsMount.current = true // 存在缓存直接跳过请求 if (allPosts.length > 0) return async function loadInitialPosts() { try { const loadPostResponse = await fetch(`${process.env.REACT_APP_API_BACKENDURL}/blob/load/all/post/0/${increment}`, { method: "GET", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("uuid") } }) const loadPostData = await loadPostResponse.json() if (loadPostResponse.status === 200 && loadingPostIsMount.current) { const sortedPosts = loadPostData.data.sort((a, b) => b.time - a.time) setAllPosts(sortedPosts) setCurrentPage(2) // 写入缓存 sessionStorage.setItem("_users_posts", JSON.stringify(sortedPosts)) sessionStorage.setItem("_users_posts_page", "2") sessionStorage.setItem("_users_posts_increment", String(increment)) } } catch (err) { console.error(err) } } loadInitialPosts() return () => { loadingPostIsMount.current = false } }, []) // 无限滚动加载更多逻辑 useEffect(() => { // 跳过首屏初始化阶段的无效触发 if (currentPage === 0) return if (increment === 2 && allPosts.length > 0) return async function loadMorePosts() { try { const loadPostResponse = await fetch(`${process.env.REACT_APP_API_BACKENDURL}/blob/load/all/post/${currentPage}/${increment}`, { method: "GET", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("uuid") } }) const loadPostData = await loadPostResponse.json() if (loadPostResponse.status === 200 && loadingPostIsMount.current) { const sortedNewPosts = loadPostData.data.sort((a, b) => b.time - a.time) // 拼接新老数据,不覆盖已有内容 const updatedPosts = [...allPosts, ...sortedNewPosts] setAllPosts(updatedPosts) const newPage = currentPage + 2 setCurrentPage(newPage) // 更新缓存 sessionStorage.setItem("_users_posts", JSON.stringify(updatedPosts)) sessionStorage.setItem("_users_posts_page", String(newPage)) sessionStorage.setItem("_users_posts_increment", String(increment)) } } catch (err) { console.error(err) } } loadMorePosts() return () => { loadingPostIsMount.current = false } }, [increment]) // 无限滚动触发函数 function loadMorePost() { setIncrement(prev => prev + 2) } // InfiniteScroll组件直接读取allPosts长度作为dataLength <InfiniteScroll dataLength={allPosts.length} next={loadMorePost} hasMore={true} loader={<Spinner name="three-bounce" />} className="md:ml-[8rem] ml-[0rem]" endMessage={<p>loading end</p>} />
内容的提问来源于stack exchange,提问作者hydraCode
相关产品推荐
相关产品推荐

