解决React中useEffect调用setState引发的无限渲染问题
问题背景
组件运行时出现以下警告:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
问题出在这段useEffect代码:
useEffect(() => { if (data?.meta) { setPagination({ showPrev: data.meta.has_previous_page, showNext: data.meta.has_next_page }); } }, [data?.meta]);
问题原因
每次API请求返回的data.meta都是新的对象引用(即使里面的has_previous_page和has_next_page值没有变化),导致useEffect的依赖data?.meta被判定为“变化”,进而触发setPagination更新状态。状态更新会引发组件重渲染,重渲染后data.meta又变成新引用,再次触发useEffect,形成无限循环。
解决方案
方案1:依赖具体属性而非整个对象
直接监听data.meta里的具体布尔值,只有当这些值真正变化时才执行更新:
useEffect(() => { if (data?.meta) { setPagination({ showPrev: data.meta.has_previous_page, showNext: data.meta.has_next_page }); } }, [data?.meta.has_previous_page, data?.meta.has_next_page]);
优点:简单直接,避免对象引用变化导致的无效触发,推荐使用。
方案2:手动浅比较+useRef跟踪旧值
用useRef存储上一次的meta状态,在useEffect里做浅比较,只有属性值变化时才更新:
import { useRef } from 'react'; // ... const prevMetaRef = useRef<typeof data.meta>(); useEffect(() => { if (data?.meta) { const currentMeta = data.meta; // 对比关键属性是否变化 const hasUpdated = prevMetaRef.current?.has_previous_page !== currentMeta.has_previous_page || prevMetaRef.current?.has_next_page !== currentMeta.has_next_page; if (hasUpdated) { setPagination({ showPrev: currentMeta.has_previous_page, showNext: currentMeta.has_next_page }); prevMetaRef.current = currentMeta; } } }, [data?.meta]);
优点:适合需要依赖整个对象但要避免无效更新的场景,灵活性高。
方案3:直接派生计算,无需单独状态
由于pagination完全由data.meta派生而来,不需要用useState维护,直接在渲染时计算即可:
// 移除原有的const [pagination, setPagination] = useState<PaginatorProps>(); // 在需要使用pagination的地方直接计算 const pagination = data?.meta ? { showPrev: data.meta.has_previous_page, showNext: data.meta.has_next_page } : undefined;
优点:最简洁,减少状态管理成本,从根源避免循环问题,优先考虑。
内容的提问来源于stack exchange,提问作者user19625670

