React滚动加载电影:page状态更新但API请求仍用page=1的问题
问题分析
核心问题:闭包捕获旧状态
fetchMovies函数在组件首次渲染时被定义,此时它的闭包环境捕获了page的初始值1。后续虽然通过setPage更新了状态,但fetchMovies函数本身没有重新创建——它一直沿用首次定义时捕获的page=1,所以每次API请求的参数始终是page=1。状态更新方式的正确性
你使用setPage((prevPage) => prevPage + 1)这种函数式更新的写法是完全正确的。这种写法基于最新的前一次状态计算新值,避免了直接依赖page变量可能导致的状态不同步问题,是处理依赖前状态的标准写法。
解决方案
要解决这个问题,需要让fetchMovies每次都能获取到最新的page值,同时处理滚动事件的闭包问题,具体方案如下:
方案一:用useCallback包裹函数,更新依赖
通过useCallback让fetchMovies在page变化时重新创建,确保捕获最新的page值,同时修复滚动事件的闭包问题:
import { useState, useEffect, useCallback } from "react"; // ... const fetchMovies = useCallback(async () => { const options = { method: "GET", headers: { accept: "application/json", Authorization: "Bearer " + bearerToken, }, }; setLoading(true); try { const res = await fetch( `https://api.themoviedb.org/3/movie/popular?language=en-US&page=${page}`, options ); const data = await res.json(); setPage(prev => prev + 1); setMovies(prev => [...prev, ...data.results]); } catch (error) { console.error(error); } setLoading(false); }, [page, bearerToken]); const handleScroll = useCallback(() => { const { scrollTop, clientHeight, scrollHeight } = document.documentElement; if (scrollTop + clientHeight >= scrollHeight - 5 && !loading) { fetchMovies(); } }, [fetchMovies, loading]); useEffect(() => { window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, [handleScroll]);
方案二:请求前缓存当前页码
如果不想频繁创建函数,也可以在fetchMovies内部先缓存当前的page值,确保请求用的是调用函数时的最新页码:
const fetchMovies = async () => { const currentPage = page; // 缓存当前page值 const options = { /* ... */ }; setLoading(true); try { const res = await fetch( `https://api.themoviedb.org/3/movie/popular?language=en-US&page=${currentPage}`, options ); const data = await res.json(); setPage(prev => prev + 1); setMovies(prev => [...prev, ...data.results]); } catch (error) { console.error(error); } setLoading(false); };
这种方式不需要额外的Hook,适合简单场景,即使page在请求过程中更新,也不会影响当前请求的页码。
内容的提问来源于stack exchange,提问作者sameer sheikh
相关产品推荐
相关产品推荐

