Search code examples
reactjsreduxreact-redux

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

  • 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.