gaearon/react-hot-loader

RHL is recreating class arrow functions which breaks event listeners

Open

#1.273 aberto em 27 de jun. de 2019

Ver no GitHub
 (10 comments) (0 reactions) (1 assignee)JavaScript (844 forks)batch import
discussionhelp wanted

Métricas do repositório

Stars
 (12.265 stars)
Métricas de merge de PR
 (Nenhuma PRs mesclada em 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.

Guia do colaborador