gaearon / react-hot-loader

Tweak React components in real time. (Deprecated: use Fast Refresh instead.)

Home Page:http://gaearon.github.io/react-hot-loader/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

hmr will refresh the entire component tree

282159468 opened this issue · comments

Description

any change in the components no matter how deeply nested. hmr will refresh the entire component tree. it should only update the change which caused it.

// indes.js
import './rhl-config'
import 'react-hot-loader/patch'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'

ReactDOM.render(<App />, document.getElementById('root'))

// app.js
import React, { PureComponent } from 'react'
import { hot } from 'react-hot-loader/root'
import B from './b'

class App extends PureComponent {
  constructor(props) {
    super(props)
    console.log('App.constructor exec')
  }

  render() {
    return (
      <div>
        App
        <B />
      </div>
    )
  }
}
export default hot(App)


// b.js
import React, { PureComponent } from 'react'
import C from './c'

class B extends PureComponent {
  constructor(props) {
    super(props)
    console.log('B.constructor exec')
  }

  render() {
    return (
      <div>
        B PureComponent
        <C />
      </div>
    )
  }
}

export default B

// c same as B

Expected behavior

HMR should only update the piece of code which was changed. or view which was updated.

Actual behavior

HMR refreshes entire component tree on every change made to any component in that tree. even if its a small change in one character in view. it refreshes the whole component tree.
client:55 [WDS] App updated. Recompiling...
reloadApp.js:19 [WDS] App hot update...
log.js:24 [HMR] Checking for updates on the server...
c.js:6 C.constructor exec
c.js:6 C.constructor exec
b.js:7 B.constructor exec
b.js:7 B.constructor exec
app.js:8 App.constructor exec
app.js:8 App.constructor exec
image

changes component C will trigger entire tree refresh. and it's double
i am not using code lazy spliting and other lib.

Environment

React Hot Loader version:4.12.13

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

  1. node -v:10.15.3
  2. npm -v:6.4.1

Then, specify:

  1. Operating system: windows 10
  2. Browser and version:chrome77.0.3865.90

Reproducible Demo

https://github.com/282159468/rhl-tree

According to webpack hmr mechanism and js closure feature,
change c.js would cause b.js changed and change b.js would cause app.js changed.
And hot(App) detects whether app.js is changed.
So it's expected behavior that change c.js cause refreshes the tree from c to root.

But I found another problem: when change app.js, ComponentB and ComponentC shouldn't be rerendered. But it actually rerendered.
I thought it might be a problem with webpack hmr mechanism: update app.js causing re-evaluation of b.js and c.js, so we got a different reference of ComponentB and ComponentC, and react's diff algorithm compares ComponentType and it detects the type in v-dom changed from older ComponentB reference to newer ComponentB reference.

Conclusion:
1.Modify c.js causing app.js to rerender is expected behavior in rhl because
1⃣️:js's closure feature(App is depend on ComponentB, ComponentB is depend on ComponentC) and
2⃣️:webpack hmr mechanism(app.js is depend on c.js).
2.Modify app.js causing ComponentC to change is expected behavior in webpack hmr mechanism.

Thus, this behavior is somehow the expected behaivor.
If we want to achieve the real "update the piece of code which was changed", then we might should file a issue to webpack to enhance the hmr mechanism.