react-hot-loader: Infinite render loop in 4.12.1 and up.

Description

For versions 4.12.1 and up (last tested at 4.12.5), my app goes into a infinite render loop, resulting in “Invariant: Maximum update depth exceeded” error displaying in the rhl redbox anytime it hot reloads, or on pages that makes change to redux state. It works fine in 4.12.0.

The error is the result of one of the root components of my app, exported with react-redux connect:

export default connect(mapStateToProps, undefined, undefined, {pure: false})(MyComponent)

If I change the pure option to true, the problem disappears.

export default connect(mapStateToProps, undefined, undefined, {pure: true})(MyComponent)

Don’t have time right now to create a reproducible demo, I’ll look into it if its needed.

Expected behavior

It’s shouldn’t break.

Actual behavior

It goes into an infinite render loop.

Environment

React Hot Loader version: 4.12.1 and up

Run these commands in the project folder and fill in their results:

  1. node -v: v11.9.0
  2. npm -v: 6.5.0

Then, specify:

  1. Operating system: MacOS Mojave
  2. Browser and version: Chrome/Firefox latest

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 11
  • Comments: 21

Commits related to this issue

Most upvoted comments

I was able to reproduce it with 1 component, here are the details:

  • using babel plugin "react-hot-loader/babel"
  • using webpack alias: 'react-dom': '@hot-loader/react-dom',
  • react-redux@7.1.0, redux@4.0.1, @hot-loader/react-dom@16.8.6, @babel/core@7.5.0, webpack@4.35.2, webpack-dev-server@3.7.2
  • webpack command webpack-dev-server --hot --config webpack/webpack.config.dev.js

app.js

import { hot } from 'react-hot-loader/root'
import { Provider } from 'react-redux'

import store from './store'

import { MyComponent1 } from './test'

const App = () => (
  <Provider store={store}>
    <MyComponent1>
        test
    </MyComponent1>
  </Provider>
)

export default hot(App)

test.js

import React from 'react'
import { connect } from 'react-redux'

export class MyComponent1 extends React.Component {
  render() {
    return (
      <div>
        {this.props.children}
      </div>
    )
  }
}

MyComponent1 = connect(state => state, undefined, undefined, {pure: false})(MyComponent1)

When I update the test component:

(MyComponent1, in Connect(MyComponent1) (created by App)) Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

// edit: If any of the child components of MyComponent1 updates the redux store, it also triggers the infinite loop.

Hello I got the same issue recently. 4.12.6 to 4.12.13 are working correctly, but from 4.12.14, it starts doing the infinite loop again. I’ve tested up to 4.12.21.

Did something huge change between 4.12.13 and 4.12.14?

Notice that 4.12.14 freezes the tab with infinite loop, with 4.12.15, tab is not freezed but infinite loop can be seen.

Can we reopen this issue?

Confirming, the problem no longer occurs in my code.

v4.12.6 released. In my tests everything is good.

Problem has been triaged - related to hooks reloading Fixing…

RHL is adding a dependency the any hook with dependency which represent “hot-reloading sequence” There are 3 ways to track that sequence:

  1. on component register(babel plugin). That’s a wrong way, but it’s in use right now for almost all cases. Does not play well with code splitting - it shall not activate any hot replacement logic.
  2. on component secondary register, that’s already much closer to the realty. This thing is used to track “sequence” for hooks.
  3. somewhere in hot, ie track HMR events. But some applications (react-static) uses HMR without hot helper.

The problem is with .2 - component “autodiscovery” breaks logic, moving generation forward during hot replacement process, thus causing infinite loop.

Fix - dont change it if hot replacement is already enabled.

Similar issue here – but instead of getting an update depth error, Chrome just freezes the page and pegs my CPU at 100%. If I throw a couple console logging lines into my component hierarchy, I can see the whole tree rendering 20-30x/second.

Rolling back to my dependencies from last week (react-hot-loader@4.12.0, babel plugins at 7.4.5, etc.) everything works fine. Upgrading react-hot-loader to anything newer (4.12.1-4.12.5) causes the issue. I’m not using react-redux, though it looks like one of my dependencies (react-beautiful-dnd) is.

Also, disabling the 'react-dom': '@hot-loader/react-dom' webpack alias seems to at least stop the infinite loop (though probably breaks hooks support).