useEffect依赖Redux数组引发无限循环的问题及解决方法
解决Redux useSelector引发的useEffect无限循环问题
问题场景
在Redux中存储数组后,通过useSelector获取数据并传入lookupTypeData工具函数,得到masterStatusOptions数组。将该数组作为useEffect的依赖项后,每次组件重渲染时,lookupTypeData都会返回一个内存地址全新的数组(即便内容完全一致),导致useEffect反复触发;结合setStatusOptions引发的重渲染,最终陷入无限循环。
组件代码示例
const masterStatusOptions = lookupTypeData(useSelector((state) => state.lookup.lookup), 'user_status'); // 返回查找值数组 const [statusOptions, setStatusOptions] = useState(); useEffect(()=>{ if(masterStatusOptions?.length){ let masterArr = masterStatusOptions.filter((item) => item.priority !== 999); console.log(masterArr); setStatusOptions(masterArr); // 触发重渲染后,masterStatusOptions因返回新数组地址再次变化,无限循环由此产生 } },[masterStatusOptions])
lookupTypeData工具函数代码
export const lookupTypeData = (lookupData, type, idKey = "lookupId") => { console.log({lookupData}) if (lookupData?.length) { let filtered = []; for (let i = 0; i < lookupData.length; i++) { const item = lookupData[i]; if (item.lookupType === type && lookupData[i].isActive && lookupData[i].isVisible) { const newItem = { ...item, value: item[idKey], label: item.lookupDisplayValue ? item.lookupDisplayValue : item.lookupValue, disabled:item.isDisabled }; filtered.push(newItem); } } filtered.sort((x, y) => x.priority - y.priority); console.log({filtered}) return filtered; } }
解决方法
方法1:为useSelector添加自定义比较逻辑
useSelector默认使用浅比较,只要数组内存地址变化就会判定值更新。可以传入自定义比较函数,只在数组内容真正变化时才触发更新:
// 方式1:使用Redux自带的shallowEqual做浅比较 import { shallowEqual, useSelector } from 'react-redux'; const rawLookupData = useSelector((state) => state.lookup.lookup, shallowEqual); const masterStatusOptions = lookupTypeData(rawLookupData, 'user_status'); // 方式2:使用深比较(适合数组内对象有深层变化的场景) import { useSelector } from 'react-redux'; import isEqual from 'lodash/isEqual'; const rawLookupData = useSelector((state) => state.lookup.lookup, isEqual); const masterStatusOptions = lookupTypeData(rawLookupData, 'user_status');
方法2:用useMemo缓存masterStatusOptions计算结果
通过useMemo缓存lookupTypeData的返回值,只有当原始的rawLookupData真正变化时,才重新生成新数组:
const rawLookupData = useSelector((state) => state.lookup.lookup); const masterStatusOptions = useMemo(() => { return lookupTypeData(rawLookupData, 'user_status'); }, [rawLookupData]); // 仅依赖原始Redux数据,避免无意义的数组重新生成
方法3:移除额外state,直接在渲染阶段处理逻辑
如果statusOptions只是masterStatusOptions的过滤结果,可以直接在渲染时计算,无需通过useEffect和额外state存储,从根源避免循环:
const rawLookupData = useSelector((state) => state.lookup.lookup); const masterStatusOptions = useMemo(() => { return lookupTypeData(rawLookupData, 'user_status'); }, [rawLookupData]); // 直接在渲染时过滤,省去useEffect和setStatusOptions const statusOptions = masterStatusOptions?.length ? masterStatusOptions.filter(item => item.priority !== 999) : undefined;
方法4:缓存lookupTypeData的执行结果(可选)
通过函数记忆化(memoization)缓存工具函数的返回值,相同输入返回同一个内存地址的数组:
import memoize from 'lodash/memoize'; export const lookupTypeData = memoize((lookupData, type, idKey = "lookupId") => { // 原函数逻辑保持不变 }, (lookupData, type, idKey) => { // 生成唯一缓存key,注意:大数组用JSON.stringify可能影响性能,可根据实际场景调整 return `${type}-${idKey}-${JSON.stringify(lookupData)}`; });
内容的提问来源于stack exchange,提问作者Akshay Seth
相关产品推荐
相关产品推荐

