4

I am trying to update the state (tableColumnConfiguration) inside useEffect and then pass on that state to the child component, but this code throws a "Maximum update depth exceeded" error and the app freezes without being able to click anything on screen.

const[environmentTableColumns, setEnvironmentTableCoulmns] = useState(environmentConditionsColumns);

const {
  data: conditionTypeData,
  loading: loadingConditionTypeData,
  errorRedirect: conditionTypeDataErrorRedirect
} = useSectionEnumQuery('conditionType'); // this is API call


useEffect(() => {
  if (conditionTypeData) {
    let data; 
    let updatedEnvironmentColumnConfiguration = environmentConditionsColumns;
    updatedEnvironmentColumnConfiguration = updatedEnvironmentColumnConfiguration.map(item => {
      if (item.dataIndex === 'conditionType') {
        data = conditionTypeData;
      }
      return data
        ? {
          ...item,
          render: text => {
            return renderEnum(text, data);
          }
        }
        : item;
    });
    setEnvironmentTableCoulmns(updatedEnvironmentColumnConfiguration); // here i am setting the state 
  }
}, [conditionTypeData])

Child component :

<SpaceTypeTable
  values={values}
  isReadOnly={isReadOnly}
  isProjectSystem={isProjectSystem}
  tableTitle="Environment Criteria"
  mappedLibrarySourceArray="environments"
  sourceRender={p => renderGuidelineItem(p, true)}
  tableColumns={environmentTableColumns} // here i am passing the column configuration
  section={MASTER_SECTIONS.LIBRARY_ENVIRONMENT}
  guidelines={guidelines}
  form={form}
  handleWarning={handleWarning}
/>

What's causing this useEffect loop?

Update : UseSectionEnumQuery :

export const useSectionEnumQuery = resultFieldName => {
  const { data: result, loading, error } = useQuery(ENUM_TYPES(resultFieldName));
  const data = result?.[resultFieldName] && sortBy(result[resultFieldName], o => o.label);
  const errorRedirect = error && errorRedirectElement(error, resultFieldName);
  return { loading, data, errorRedirect };
};
Glory Raj
  • 17,397
  • 27
  • 100
  • 203
  • It's not necessarily obvious where it's happening, but in cases like this it almost always is setting a state in useeffect triggers a rerender which causes the effect to run again which sets the state again and so on. – Jared Smith Dec 16 '20 at 00:32
  • it is because of that useeffect i am getting the above error – Glory Raj Dec 16 '20 at 00:34
  • Yes, that's what I'm saying. If you set a state in a useeffect handler that the useeffect handler has in its dependency array, even indirectly, it will run in an infinite render loop. – Jared Smith Dec 16 '20 at 00:38
  • i put api result as a dependency for useeffect – Glory Raj Dec 16 '20 at 00:38
  • `conditionTypeData` is probably creating a new reference on each render and since it's a dependency in your `useEffect`, it will cause a loop. Can we see your `useSectionEnumQuery` hook? – Todd Skelton Dec 16 '20 at 00:46
  • @ToddSkelton i just updated question with useSectionEnumQuery hook – Glory Raj Dec 16 '20 at 00:47

1 Answers1

2

This line is causing your problem.

const data = result?.[resultFieldName] && sortBy(result[resultFieldName], o => o.label);

data will be a new reference each render and it's going to trigger your useEffect every render because data is conditionTypeData and it's in your dependencies.

Can you try memoizing the value, so it only changes when result changes.

export const useSectionEnumQuery = resultFieldName => {
  const { data: result, loading, error } = useQuery(ENUM_TYPES(resultFieldName));
  const data = useMemo(() => result?.[resultFieldName] && sortBy(result[resultFieldName], o => o.label), [result, resultFieldName]);
  const errorRedirect = useMemo(() => error && errorRedirectElement(error, resultFieldName), [error, resultFieldName]);
  return { loading, data, errorRedirect };
};
Todd Skelton
  • 6,839
  • 3
  • 36
  • 48