React网站出现Maximum call stack size exceeded错误求助
问题分析与修复
根本原因
你遇到的栈溢出问题,核心是**useSelector每次都返回新对象**,导致useEffect的依赖photos永远在变化,触发无限循环:
- 你用
{ ...state.data }在useSelector里创建了新对象,组件每次渲染都会生成一个全新的引用 - 这个新引用会触发依赖为
photos的useEffect执行 useEffect里调用setImgList更新本地状态,导致组件重新渲染- 重新渲染又会生成新的
photos对象,循环往复直到栈溢出
修复方案
方案1:直接引用Redux状态,避免创建新对象
既然你只是取state.data,没必要用扩展运算符克隆,直接返回原引用即可——Redux状态是 immutable 的,只有数据真的变化时引用才会更新:
import { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { fetchDataHandler } from "./store/actions"; const PhotoWrapper = () => { const dispatch = useDispatch(); const [imgList, setImgList] = useState([]); // 直接返回state.data,不创建新对象 const photos = useSelector((state) => state.data); useEffect(() => { dispatch(fetchDataHandler()); }, [dispatch]); useEffect(() => { const topImages = photos[0]?.map((photo) => ( <img src={photo?.url} alt={photo?.title} key={photo.id} /> )); setImgList(topImages); }, [photos]); return <div>{imgList}</div>; }; export default PhotoWrapper;
⚠️ 注意:给img元素加上key属性,避免React警告。
方案2:如果需要转换状态结构,使用浅比较
如果确实需要对Redux状态做结构转换(比如只取部分字段),可以用Redux提供的shallowEqual函数,让useSelector比较对象内容而非引用:
import { useEffect, useState } from "react"; import { useDispatch, useSelector, shallowEqual } from "react-redux"; import { fetchDataHandler } from "./store/actions"; const PhotoWrapper = () => { const dispatch = useDispatch(); const [imgList, setImgList] = useState([]); // 使用shallowEqual做浅比较 const photos = useSelector((state) => ({ ...state.data }), shallowEqual); useEffect(() => { dispatch(fetchDataHandler()); }, [dispatch]); useEffect(() => { const topImages = photos[0]?.map((photo) => ( <img src={photo?.url} alt={photo?.title} key={photo.id} /> )); setImgList(topImages); }, [photos]); return <div>{imgList}</div>; }; export default PhotoWrapper;
方案3:去掉本地状态,直接在渲染时处理
其实你完全不需要本地状态imgList,直接在渲染阶段处理photos即可,减少不必要的状态和useEffect:
import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { fetchDataHandler } from "./store/actions"; const PhotoWrapper = () => { const dispatch = useDispatch(); const photos = useSelector((state) => state.data); useEffect(() => { dispatch(fetchDataHandler()); }, [dispatch]); return ( <div> {photos[0]?.map((photo) => ( <img src={photo?.url} alt={photo?.title} key={photo.id} /> ))} </div> ); }; export default PhotoWrapper;
这个方案最简洁,也从根源上避免了循环问题。
内容的提问来源于stack exchange,提问作者Prachi Tripathi
相关产品推荐
相关产品推荐

