Discussion: useEffect hook with array dependency that has a default value causes render loop
See original GitHub issueSo maybe this is not a bug (because it’s default JavaScript behavior) but a pitfall that should be documented in the useEffect
-section of the hooks documentation. I’m not quite sure because it feels like react should handle this as default value assignment on destructuring functional component props is recommended.
When a useEffect
-Hook that has an array-prop with a default value as one of its dependencies calls a function mutating any other prop, a render loop will occur.
React version: 16.12.0
Steps To Reproduce
- Create a functional component that has 3 props: – an array prop that has a default value (created within destructuring) – any other prop (e.g. string) – a callback function to mutate the second prop
- Have an
useEffect
function that calls the callback to mutate the second prop. TheuseEffect
needs to have the array prop as one of its dependencies
Link to code example:
https://codesandbox.io/s/smoosh-field-fqduq
The current behavior
When the default value for the array prop is set to an “anonymous” array (or object) inside of the props destructuring, the useEffect
will always trigger and so the second prop will be changed via the callback function and a re-render will happen. Then the effect will be triggered again as the value for the array will have changed again.
The expected behavior
Default values for arrays (and objects) can be assigned within the destructuring of the props without causing re-renders (or even render loops) when the mentioned useEffect
is not present (like, why should it trigger a re-render?).
I’d expect react to behave the same in this case (don’t compare empty arrays/objects by reference). If not, there should be at least a warning in the docs of useEffect
that this is something to look out for.
Workaround
If the default prop is saved as a variable outside the functional component and then assigned as the default value (read: if you have referential equality), the issue does not occur obviously (default JavaScript behavior).
Issue Analytics
- State:
- Created 4 years ago
- Reactions:15
- Comments:12 (1 by maintainers)
To avoid this you can declare it outside e.g.
Consumers of your component might still encounter this pitfall when using
<MyComponent arrayProp={[]} />
ifMyComponent
triggers a re-render in its owner as well.Sure, this would be correct but also a bit too much hassle where just having a reference to an empty array would also help. Note: having an empty array or even object as default is something I repeatedly see being done wrongly as mentioned in the start of the topic. As beginners would run into this, I don’t think it is very beginner-friendly to have them having to work around this (e.g. using
useMemo
). I guess this would just lead to beginners always wrapping everything inuseMemo
, trying to fight unnecessary re-renders which will just cause memory issues in the long run.@tejas-bontadka, @martinjaime is this what you’ve been thinking of?
My whole point is not that there are no solutions to this but that this is nothing a beginner would know or work around effectively (without developing potentially bad habits like using
useMemo
everywhere). But that’s just an assumption on my side (based on what I’ve seen), which should of course be discussed.