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.