'Why componentDidMount gets called multiple times in react.js & redux?
I read componentDidMount
gets called only once for initial rendering but I'm seeing it's getting rendered multiple times.
It seems I created a recursive loop.
- componentDidMount dispatches action to fetch data
- upon receiving the data, it fires success action to store the data in redux state.
- a parent react component is connected to redux store and has
mapStateToProps
for the entry that just changed in the above step - parent renders child components (which is programmatically selected via variable)
- the child component's componentDidMount gets called again
- it dispaches action to fetch data
I think that's what's happening. I may be wrong.
How can I stop the loop?
Here's the code for programmatically rendering child components.
function renderSubviews({viewConfigs, viewConfig, getSubviewData}) {
return viewConfig.subviewConfigs.map((subviewConfig, index) => {
let Subview = viewConfigRegistry[subviewConfig.constructor.configName]
let subviewData = getSubviewData(subviewConfig)
const key = shortid.generate()
const subviewLayout = Object.assign({}, subviewConfig.layout, {key: key})
return (
<div
key={key}
data-grid={subviewLayout}
>
<Subview
{...subviewData}
/>
</div>
)
})
}
Solution 1:[1]
A component instance will only get mounted once and unmounted when it gets deleted. In your case it gets deleted and recreated.
The point of the key
prop is to help React find the previous version of the same component. That way it can update a previous component with new props rather than create a new one.
React can often work fine without a key, the exception being a list with items. It wants a key there so it can keep track when items get rearranged, created or deleted.
In your case, you are explicitly telling React that your component is different from the previous one. You're giving a new key on each render. This forces React to treat the previous instance as having been deleted. Any children of that component are also unmounted and dismantled.
What you ought to do is not (ever) generate a key at random. Keys should always be based on the identity of the data a component is displaying. If it's not a list item you probably don't need a key. If it is a list item, it's much better to use a key derived from the data's identity, such as an ID property, or maybe a combination of multiple fields.
If generating a random key would have been the correct thing to do, React would have just taken care of that for you.
You should place your initial fetch code in the root of your React tree, usually that's App
. Don't put it in some random child. At least you should put it in a component that exist for the lifetime of your app.
The main reason to put it in componentDidMount
is so it doesn't run on the server, because server-side components never get mounted. This is important for universal rendering. Even if you're not doing this now, you might do this later, and being prepared for it is a best practice.
Solution 2:[2]
Just a quote:
It's just because we do 2 initial renders in dev mode to avoid getting warnings about mismatched client/server markup when we introduce the devtools. Doesn't affect production.
First picture - development mode, second one - production mode.
Solution 3:[3]
I found out that multiple componentDidMount
calls can be caused by using <React.StrictMode>
around the component. After removing it double calls are gone.
Seems like this happens only development environment, while in production componentDidMount
is called only once even with <React.StrictMode>
.
"This was tested with React 18.1.0"
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | James Bond |
Solution 3 | Ali Alizadeh |