React函数组件用默认参数搭配useEffect出现无限循环该如何解决?
React函数组件对象类型默认参数触发useEffect无限重渲染的规避方案
以下是除defaultProps外的几个常用最优方案,可根据场景选择:
方案1:将默认对象提取到组件外部定义
组件外部的常量仅会初始化一次,全生命周期内引用稳定,不会出现每次渲染生成新实例的问题,适合默认值不需要依赖组件内部状态/属性的场景。
// 组件外定义稳定的默认对象 const DEFAULT_QUERY = { page: 1, pageSize: 10 }; function ListComponent(query = DEFAULT_QUERY) { useEffect(() => { // 副作用逻辑 getList(query); }, [query]); return <div>列表组件</div> }
方案2:解构参数后仅将原始类型值放入依赖数组
如果副作用逻辑仅用到参数的部分属性,可直接解构出对应原始类型的属性,给原始类型设置默认值,依赖数组仅传入这些原始值即可,完全避免对象引用变化的影响,是性能最优的方案。
function ListComponent(query = {}) { // 解构出需要的属性,给原始类型设默认值 const { page = 1, pageSize = 10 } = query; useEffect(() => { getList({ page, pageSize }); // 仅依赖原始类型值,不会触发无效重渲染 }, [page, pageSize]); return <div>列表组件</div> }
方案3:用useMemo缓存最终参数对象
如果默认值需要依赖组件内部状态、props做动态计算,可用useMemo缓存处理后的参数对象,依赖数组仅传入计算过程用到的原始类型值即可。
function ListComponent(initQuery) { const [userCustomSize, setUserCustomSize] = useState(10); // 缓存参数对象,仅当依赖的原始值变化时才生成新对象 const finalQuery = useMemo(() => { return { page: 1, pageSize: userCustomSize, ...initQuery }; }, [initQuery?.page, initQuery?.pageSize, userCustomSize]); useEffect(() => { getList(finalQuery); }, [finalQuery]); return <div>列表组件</div> }
方案4:深比较判断对象实际变化
如果必须将完整对象作为依赖、且对象结构复杂属性多,可在useEffect内通过深比较判断对象内容是否真的发生变化,仅内容变化时才执行副作用逻辑,适合对性能影响不大的复杂对象场景。
import isEqual from 'lodash/isEqual'; function ListComponent(query = { page: 1, pageSize: 10 }) { const prevQueryRef = useRef(); useEffect(() => { // 仅对象内容实际变化时才执行副作用 if (!isEqual(query, prevQueryRef.current)) { prevQueryRef.current = query; getList(query); } // 确认逻辑无误可加以下注释忽略eslint依赖警告 // eslint-disable-next-line react-hooks/exhaustive-deps }, [query]); return <div>列表组件</div> }
内容的提问来源于stack exchange,提问作者Auskennfuchs
相关产品推荐
相关产品推荐

