Skip to content

RHL is recreating class arrow functions which breaks event listeners #1273

Open
@gch1p

Description

@gch1p

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.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions