使用useEffect预加载API数据无效,点击按钮仍耗时6秒求排查
问题分析与解决
你的代码逻辑上应该在组件挂载时通过useEffect预加载API数据,但点击按钮仍需等待,核心问题大概率是按钮点击时重复发起了API请求,而非直接复用预加载好的本地数据。
先确认预加载是否生效
打开浏览器控制台,组件加载时如果能看到hello和RUNNING HANDLER的打印,以及随机数输出,说明预加载已经执行,数据已经存在movies状态里了。如果没看到这些日志,检查组件是否有条件渲染逻辑导致useEffect未触发。
修复方案
方案1:按钮直接复用预加载数据
如果按钮的作用是展示数据,完全不需要再调用fetchMoviesHandler,只需要控制一个显示状态即可:
const [showMovies, setShowMovies] = useState(false); // 按钮点击事件 const handleShowMovies = () => { setShowMovies(true); }; // 渲染部分 {showMovies && !isLoading && ( <div> {movies.map(movie => ( <div key={movie.id}> <h3>{movie.title}</h3> <p>{movie.openingText}</p> </div> ))} </div> )}
方案2:让请求函数优先用缓存数据
如果按钮必须保留触发请求的逻辑,修改fetchMoviesHandler,先判断本地是否已有数据,有则直接返回:
const fetchMoviesHandler = useCallback(async () => { // 优先用本地缓存的数据 if (movies.length > 0) { console.log("使用预加载缓存数据"); return; } const random = Math.random(); console.log(random); console.log("RUNNING HANDLER"); try { setError(null); setIsLoading(true); const response = await fetch(`https://swapi.dev/api/films`); if (!response.ok) { throw new Error("Something went wrong"); } const data = await response.json(); const transformedMovies = data.results.map((movieData) => { return { id: movieData.episode_id, title: movieData.title, openingText: movieData.opening_crawl, releaseDate: movieData.release_date, }; }); setMovies(transformedMovies); setIsLoading(false); } catch (error) { setError(error.message); setIsLoading(false); console.log(error); } }, [movies]); // 依赖添加movies,确保状态更新时函数同步
额外优化:区分预加载和按钮触发的加载状态
预加载时的loading状态如果不想让用户感知,可以新增独立的状态变量:
const [isPreloading, setIsPreloading] = useState(true); const fetchMoviesHandler = useCallback(async () => { try { const response = await fetch(`https://swapi.dev/api/films`); if (!response.ok) { throw new Error("Something went wrong"); } const data = await response.json(); const transformedMovies = data.results.map((movieData) => { return { id: movieData.episode_id, title: movieData.title, openingText: movieData.opening_crawl, releaseDate: movieData.release_date, }; }); setMovies(transformedMovies); } catch (error) { setError(error.message); console.log(error); } finally { setIsPreloading(false); } }, []); useEffect(() => { fetchMoviesHandler(); }, [fetchMoviesHandler]); // 按钮点击仅在无缓存时显示loading const handleButtonClick = () => { if (movies.length === 0) { setIsLoading(true); fetchMoviesHandler().then(() => setIsLoading(false)); } };
内容的提问来源于stack exchange,提问作者muzammil
相关产品推荐
相关产品推荐

