React中useEffect结合useCallback后仅渲染单条API数据的问题排查
排查React组件数据渲染异常(仅显示第一条数据)的原因
问题背景
为修复useEffect的「缺少依赖项」错误引入useCallback后,出现新问题:接口返回10条数据,但页面仅渲染第一条。
问题代码
const InStream = () => { const [gallery, setGallery] = useState([]); const [loading, setLoading] = useState(false); let [color] = useState("#1E9A4B"); const getAPI = useCallback(() => { fetch(url) .then((res) => res.json()) .then((res) => { let keys = res.values[0]; let newData = res.values.slice(1, res.values.length); let formatted = [], data = newData, cols = keys, l = cols.length; for (var i = 0; i < data.length; i++) { var d = data[i], o = {}; for (var j = 0; j < l; j++) o[cols[j]] = d[j]; formatted.push(o); setGallery(formatted); setLoading(true); } }) .catch((error) => { console.error("Error:", error); setTimeout(() => { getAPI(); }, 1000); }); }, []); useEffect(() => { getAPI(); }, [getAPI]); return (// my code rendering gallery data // ) }; export default InStream;
核心问题分析
- 循环内频繁调用setState且复用数组引用:
代码在for循环的每一次迭代中都调用setGallery(formatted),但formatted是同一个数组引用(循环外初始化的空数组,后续仅做push操作)。React的状态更新会比较前后状态的引用,当引用相同时,会判定状态未发生变化,从而不会触发组件重新渲染。只有第一次调用setGallery时,数组内容从空变为包含第一条数据,会触发渲染;后续迭代中数组内容虽增加,但引用未变,无法触发更新,最终页面仅显示第一条数据。 - useCallback依赖项缺失:
useCallback的依赖数组为空,导致内部的setGallery、setLoading、url都捕获了组件初始渲染时的闭包值,可能引发后续状态更新的异常。 - var声明的变量作用域问题:
使用var声明循环变量i、j等,会产生变量提升和函数级作用域问题,容易导致闭包逻辑错误。
修复方案
统一在循环结束后更新状态
等所有数据处理完成,得到完整的formatted数组后,再一次性调用setGallery和setLoading,避免频繁触发渲染且解决引用问题。补全useCallback的依赖项
将url、setGallery、setLoading添加到useCallback的依赖数组中,确保闭包能捕获最新的状态更新函数和变量。替换var为let/const
把循环变量和临时变量的声明从var改为let/const,避免作用域问题。
修复后的代码示例
const InStream = () => { const [gallery, setGallery] = useState([]); const [loading, setLoading] = useState(false); const [color] = useState("#1E9A4B"); const getAPI = useCallback(() => { fetch(url) .then((res) => res.json()) .then((res) => { const keys = res.values[0]; const newData = res.values.slice(1); const formatted = []; const cols = keys; const l = cols.length; for (let i = 0; i < newData.length; i++) { const d = newData[i]; const o = {}; for (let j = 0; j < l; j++) { o[cols[j]] = d[j]; } formatted.push(o); } // 循环结束后统一更新状态 setGallery(formatted); setLoading(true); }) .catch((error) => { console.error("Error:", error); setTimeout(() => { getAPI(); }, 1000); }); }, [url, setGallery, setLoading]); useEffect(() => { getAPI(); }, [getAPI]); return (// 渲染gallery的代码 // ) }; export default InStream;
内容的提问来源于stack exchange,提问作者carol chaw
相关产品推荐
相关产品推荐

