You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:32:20