50

This is a follow up question to this question which is the nearest to my issue:

Infinite loop in useEffect

I am creating a small React.js app to study the library. I'm getting this warning:

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

I got a functional component, in which there is this code:

const propertiesMap2 = new Map([  //There is also propertiesMap1 which has the same structure
["TITLE4",
    {
        propertyValues: {
            myProperty10 : "myVal1",
            myProperty11 : "myVal2",
            myProperty12 : "myVal3",                                                            
        },
        isOpen: true
    }
],
["TITLE5",
    {
        propertyValues: {
            myProperty13 : "myVal4",
            myProperty14 : "myVal5",
            myProperty15 : "myVal6",                                                             
        },
        isOpen: true
    }
],
["TITLE6",
    {
        propertyValues:{
            myProperty16 : "myVal7",
            myProperty17 : "myVal8",
            myProperty18 : "myVal9",
        },
        isOpen: true
    }                                                        
]
]);

const [properties, setPropertiesMapFunc] = useState(new Map());
useEffect(()=>
{
    let mapNum = Number(props.match.params.id);
    setPropertiesMapFunc(mapNum === 1 ?propertiesMap1 : propertiesMap2);
}, [properties]);

The correct properties map is chosen each time, but like I said I get this error. Why do I get it, if the propertiesMap is constant without anything changing, and properties was passed as a parameter to setEffect, so I thought it would only re render when something there changes..

CodeMonkey
  • 11,196
  • 30
  • 112
  • 203
  • Doesn't the Component rerender every time you call setPropertiesMapFunc? Meaning any time you setState, even if the new state is identical? – Arye Eidelman Sep 09 '19 at 11:46
  • 1
    Is the initialisation of the maps inside or outside of the component? – trixn Sep 09 '19 at 11:49
  • @trixn ok it seems to be what you said. The map was recreated every time so it caused the re render. If you add it as an answer, I will accept it – CodeMonkey Sep 09 '19 at 12:19

2 Answers2

69

Because you are creating the map objects inside of your component function they will be recreated on every render. Because of that your effect will set a new map as the new state which will in turn trigger another re-render and your effect being called again which leads to an infinite update loop.

You can move the definition of your map objects outside of your component to fix this.

trixn
  • 15,761
  • 2
  • 38
  • 55
12

properties is a Map, and when passed in dependency array to useEffect, it will be recreated on every render, and when compared, it will always be not equal to itself, since the map, same as other non-primitive types in JS are compared by reference and not by value. So this will cause the function inside the useEffect to be run on every re-render.

You'd need to wrap it into some kind of deep compare function: https://stackoverflow.com/a/54096391/4468021

Clarity
  • 10,730
  • 2
  • 25
  • 35
  • 3
    Because the map objects do not change it would in this case also be sufficient to move the initialisation of the map objects outside of the component so that they aren't recreated on every render or wrap them in `useMemo`. – trixn Sep 09 '19 at 11:49