页面加载随机生成数组对象:useState/useEffect的ESLint警告与无限循环问题
问题分析与修复方案
你的问题核心在于:filteredQuestionByCategory是组件每次渲染都会重新创建的新对象,把它加入useEffect依赖后,每次渲染都会触发useEffect,进而调用setQuestions导致组件重新渲染,形成无限循环。同时原代码里还存在直接使用状态questions来派生过滤结果的逻辑错误——初始状态为空,会导致首次渲染时逻辑异常。
下面是两种可行的修复方案:
方案一:将过滤逻辑移入useEffect内部
直接把排序、过滤的逻辑放到useEffect里,避免依赖外部派生的不稳定对象:
function QuestionPopulator({ data: { questionCollection, category } }) { const router = useRouter(); const [questions, setQuestions] = useState([]); useEffect(() => { if (questionCollection?.items?.length > 0) { setQuestions(questionCollection); } else if (category) { // 先复制数组再排序,避免修改原数据(sort是原地排序) const sortedQuestions = [...questionCollection.items].sort(() => 0.5 - Math.random()); const filteredItems = sortedQuestions.filter(item => !router.asPath.includes(item.slug)).slice(0, 4); setQuestions({ items: filteredItems }); } }, [questionCollection, category, router.asPath]); return (/* 返回渲染内容 */) }
说明
- 依赖项只保留原始的props和路由数据,这些值的引用是稳定的,不会触发不必要的
useEffect执行。 - 用扩展运算符创建数组副本,避免
sort修改原数组引发的副作用。
方案二:用useMemo缓存过滤结果
如果需要保留外部的过滤变量,可以用useMemo缓存它,确保只有依赖变化时才重新生成:
function QuestionPopulator({ data: { questionCollection, category } }) { const router = useRouter(); const [questions, setQuestions] = useState([]); const filteredQuestionByCategory = useMemo(() => { if (!category || !questionCollection?.items) return null; // 复制数组后再排序,避免修改原数据 const sortedQuestions = [...questionCollection.items].sort(() => 0.5 - Math.random()); const filteredItems = sortedQuestions.filter(item => !router.asPath.includes(item.slug)).slice(0, 4); return { items: filteredItems }; }, [category, questionCollection, router.asPath]); useEffect(() => { if (questionCollection?.items?.length > 0) { setQuestions(questionCollection); } else if (filteredQuestionByCategory) { setQuestions(filteredQuestionByCategory); } }, [questionCollection, filteredQuestionByCategory]); return (/* 返回渲染内容 */) }
说明
useMemo会缓存计算结果,只有当依赖项(category、questionCollection、router.asPath)变化时才重新生成filteredQuestionByCategory,保证引用稳定,不会触发无限循环。
额外注意事项
- 永远不要在渲染阶段直接使用
sort、reverse这类原地修改数组的方法,必须先创建数组副本,否则会导致组件渲染异常。 - 原代码中用状态
questions来派生过滤结果是错误的逻辑,应该直接基于传入的questionCollectionprops处理,避免闭包和状态更新的混乱。
不建议忽略ESLint警告,这些警告是在提示潜在的bug(比如闭包过时、不必要的重渲染),上述方案既能解决警告,又能彻底避免无限循环问题。
内容的提问来源于stack exchange,提问作者Omilight
相关产品推荐
相关产品推荐

