gaearon / gaearon/react-hot-loader
RHL is recreating class arrow functions which breaks event listeners
- Dominant language
- JavaScript
- Stars
- 12.2k
- Forks
- 775
- PR merge metrics
- No merged PRs in 30d
Description
Let's say I have a component like this:
```
class A extends Component {
componentDidMount() {
window.addEventListener('resize', this.onResize)
}
componentWillUnmount() {
window.removeEventListener('resize', this.onResize)
}
onResize = () => {
this.setState({...})
}
...
}
```
This is my babel 7 config:
```
{
presets: [
[
"@babel/preset-env",
{
loose: true,
modules: "commonjs",
targets: {
chrome: "66",
}
}
],
"@babel/preset-react"
],
plugins: [
'react-hot-loader/babel',
'add-module-exports',
["@babel/plugin-proposal-decorators", {legacy: true}],
["@babel/plugin-proposal-class-properties", {loose: true}],
]
}
```
Babel with `plugin-proposal-class-properties` transpiles above component to roughly something like this:
```
class A extends Component {
constructor() {
super()
this.onResize = () => {
this.setState({...})
}
}
componentDidMount() {
window.addEventListener('resize', this.onResize)
}
componentWillUnmount() {
window.removeEventListener('resize', this.onResize)
}
}
```
When hot reloading happens, `this.onResize` is redefined and points to a new function, not the function that was previously registered as a resize listener in didMount. When component gets unmounted, removeEventListener obviously doesn't work because this new onResize listener has never been registered, and as a result I have memory leak, React complaining about calling setState on an unmounted component and all sorts of bugs.
For now, I just hacked the RHL's code to prevent updating arrow functions at all (`src/proxy/inject.js`):
```
if (!isArrow && (
nextString !== String(prevAttr) ||
(injectedBefore && nextString !== String(injectedBefore))
))
```
Perhaps you will come up with a more proper solution.
Contributor guide
Assessment
This issue has not been assessed yet.